02数组+字符串+滑动窗口+前缀和与差分+双指针(D2_字符串(D2_刷题练习))

目录

[1. 最长公共前缀](#1. 最长公共前缀)

[1.1. 题目描述](#1.1. 题目描述)

[1.2. 解题方案](#1.2. 解题方案)

方案一:纵向对比

方案二:横向对比

方案三:最值对比

方案四:分治

方案五:二分

[1.3. 知识归纳](#1.3. 知识归纳)

[2. 左旋转字符串(简单)](#2. 左旋转字符串(简单))

[2.1. 题目描述](#2.1. 题目描述)

[2.2. 解题思路](#2.2. 解题思路)

方法一:字符串切片

方法二:列表遍历拼接

方法三:字符串遍历拼接

[3. 表示数值的字符串(中等)](#3. 表示数值的字符串(中等))

[3.1. 题目描述](#3.1. 题目描述)

[3.2. 解题思路](#3.2. 解题思路)

方法一:确定有限状态自动机

[4. 把字符串转换成整数(中等)](#4. 把字符串转换成整数(中等))

[4.1. 题目描述](#4.1. 题目描述)

[4.2. 解题思路](#4.2. 解题思路)

[5. 交替合并字符串(简单)](#5. 交替合并字符串(简单))

[5.1. 题目描述](#5.1. 题目描述)

[5.2. 解题思路](#5.2. 解题思路)

方法一:双指针

[6. 字符串的最大公因子(简单)](#6. 字符串的最大公因子(简单))

[6.1. 题目描述](#6.1. 题目描述)

[6.2. 解题思路](#6.2. 解题思路)

方法一:枚举

方法二:枚举优化

方法三:数学

[7. 反转字符串中的元音字母(简单)](#7. 反转字符串中的元音字母(简单))

[7.1. 题目描述](#7.1. 题目描述)

[7.2. 解题思路](#7.2. 解题思路)

方法一:双指针

[8. 递增的三元子序列(中等)](#8. 递增的三元子序列(中等))

[8.1. 题目描述](#8.1. 题目描述)

[8.2. 解题思路](#8.2. 解题思路)

方法一:双向遍历

方法二:贪心

[9. 压缩字符串(中等)](#9. 压缩字符串(中等))

[9.1. 题目描述](#9.1. 题目描述)

[9.2. 解题思路](#9.2. 解题思路)

方法一:双指针

[10. 反转字符串中的单词(中等)](#10. 反转字符串中的单词(中等))

[10.1. 题目描述](#10.1. 题目描述)

[10.2. 解题思路](#10.2. 解题思路)

方法一:使用语言特性

方法二:自行编写对应的函数

方法三:双端队列

[11. 字符串变形(简单)](#11. 字符串变形(简单))

[11.1. 题目描述](#11.1. 题目描述)

[11.2. 解题思路](#11.2. 解题思路)

方法一:双逆转(推荐使用)

方法二:分割字符串+栈(扩展思路)

[12. 最长公共前缀](#12. 最长公共前缀)

[12.1. 题目描述](#12.1. 题目描述)

[12.2. 解题思路](#12.2. 解题思路)

方法一:遍历查找(推荐使用)

[13. 验证IP地址](#13. 验证IP地址)

[13.1. 题目描述](#13.1. 题目描述)

[13.2. 解题思路](#13.2. 解题思路)

方法一:分割字符串比较法(推荐使用)

方法二:正则表达式(扩展思路)

[14. 大数加法](#14. 大数加法)

[14.1. 题目描述](#14.1. 题目描述)

[14.2. 解题思路](#14.2. 解题思路)

方法一:模拟法(建议使用)

[15. 无重复字符的最长子串](#15. 无重复字符的最长子串)

[15.1. 题目描述](#15.1. 题目描述)

[15.2. 解题思路](#15.2. 解题思路)

方法一:滑动窗口

[16. 找到字符串中所有字母异位词(中等)](#16. 找到字符串中所有字母异位词(中等))

[16.1. 题目描述](#16.1. 题目描述)

[16.2. 解题思路](#16.2. 解题思路)

方法一:滑动窗口

方法二:优化的滑动窗口

[17. 定长子串中元音的最大数目(中等)](#17. 定长子串中元音的最大数目(中等))

[17.1. 题目描述](#17.1. 题目描述)

[17.2. 解题思路](#17.2. 解题思路)

方法一:滑动窗口

[18. 判断是否为回文字符串](#18. 判断是否为回文字符串)

[18.1. 题目描述](#18.1. 题目描述)

[18.2. 解题思路](#18.2. 解题思路)

方法一:首尾依次比较法(推荐使用)

方法二:反转字符串比较法(扩展思路)

[19. 最小覆盖子串(较难)](#19. 最小覆盖子串(较难))

[19.1. 题目描述](#19.1. 题目描述)

[19.2. 解题思路](#19.2. 解题思路)

方法一:哈希表匹配(推荐使用)

[20. 反转字符串(入门)](#20. 反转字符串(入门))

[20.1. 题目描述](#20.1. 题目描述)

[20.2. 解题思路](#20.2. 解题思路)

解法一

解法二

解法三


1. 最长公共前缀

1.1. 题目描述

1.2. 解题方案

对于一个问题,采用一题多解,才能深刻理解其考察的知识点,进行举一反三。

最长公共前缀,可横向对比 / 竖向对比 / 最值对比 / 分治 / 二分方法来完成题解。

方案一:纵向对比
    public String longestCommonPrefix(String[] strs) {
        for (int i = 0; i < strs[0].length(); i++) {
            char ch = strs[0].charAt(i);
            for (int j = 1; j < strs.length; j++) {
                if (i == strs[j].length() || strs[j].charAt(i) != ch) return strs[0].substring(0, i);
            }
        }
        return strs[0];
    }
方案二:横向对比
    // idea6:横向扫描(暴力之一)
    public String longestCommonPrefix6(String[] strs) {
        String prefix = strs[0];

        for (String s : strs) prefix = compareStr(prefix, s);

        return prefix;
    }
    private String compareStr(String s1, String s2) {
        int idx = 0;
        while (idx < s1.length() && idx < s2.length() && s1.charAt(idx) == s2.charAt(idx)) ++idx;

        return s1.substring(0, idx);
    }
方案三:最值对比
    public String longestCommonPrefix3(String[] strs) {
        String min = strs[0], max = strs[0];

        for (String str : strs) {
            // bug1:字符串的compare和integer的不一样,并非-1 / 0 / 1,而是长度相减作为返回值。
            if (min.compareTo(str) > 0) min = str;
            if (max.compareTo(str) < 0) max = str;
        }

        return compareStr(min, max);
    }
    private String compareStr(String s1, String s2) {
        int idx = 0;
        while (idx < s1.length() && idx < s2.length() && s1.charAt(idx) == s2.charAt(idx)) ++idx;

        return s1.substring(0, idx);
    }
方案四:分治
    private String partition(String[] strs, int left, int right) {
        if (left == right) return strs[left];

        int mid = left + (right - left >> 1);
        String s1 = partition(strs, left, mid);
        String s2 = partition(strs, mid + 1, right);

        return compareStr(s1, s2);
    }
    private String compareStr(String s1, String s2) {
        int idx = 0;
        while (idx < s1.length() && idx < s2.length() && s1.charAt(idx) == s2.charAt(idx)) ++idx;

        return s1.substring(0, idx);
    }
方案五:二分
    public String longestCommonPrefix5(String[] strs) {
        String s = strs[0];
        // 有可能需要idx = 0的位置,所以也会需要s.len的位置。
        int low = 0, high = s.length();
        // 用二分法去快速寻找公共前缀的右边界。
        while (low < high) {
            int mid = low + (high - low + 1 >> 1);// 为了low不越界,这里必须+1防止死循环。
            String midStr = s.substring(0, mid);

            if (isMatchAll(strs, midStr)) low = mid;
            else high = mid - 1;
        }
        return s.substring(0, low);
    }
    private boolean isMatchAll(String[] strs, String str) {
        for (String s : strs) {
            if (s.length() < str.length() || !str.equals(s.substring(0, str.length()))) return false;
        }
        return true;
    }

1.3. 知识归纳

最长公共前缀,可横向对比 / 竖向对比 / 最值对比 / 分治 / 二分方法来完成题解。

对于分治来讲,可多线程操作加快速度;

对于二分,考察对抽象二分的理解,将二分规则进行抽象。

2. 左旋转字符串(简单)

2.1. 题目描述

2.2. 解题思路

方法一:字符串切片
class Solution {
    public String reverseLeftWords(String s, int n) {
        return s.substring(n, s.length()) + s.substring(0, n);
    }
}
方法二:列表遍历拼接
class Solution {
    public String reverseLeftWords(String s, int n) {
        StringBuilder res = new StringBuilder();
        for(int i = n; i < s.length(); i++)
            res.append(s.charAt(i));
        for(int i = 0; i < n; i++)
            res.append(s.charAt(i));
        return res.toString();
    }
}
方法三:字符串遍历拼接
class Solution {
    public String reverseLeftWords(String s, int n) {
        String res = "";
        for(int i = n; i < s.length(); i++)
            res += s.charAt(i);
        for(int i = 0; i < n; i++)
            res += s.charAt(i);
        return res;
    }
}

3. 表示数值的字符串(中等)

3.1. 题目描述

3.2. 解题思路

方法一:确定有限状态自动机
class Solution {
    public boolean isNumber(String s) {
        Map<State, Map<CharType, State>> transfer = new HashMap<State, Map<CharType, State>>();
        Map<CharType, State> initialMap = new HashMap<CharType, State>() {
  
  {
            put(CharType.CHAR_SPACE, State.STATE_INITIAL);
            put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
            put(CharType.CHAR_POINT, State.STATE_POINT_WITHOUT_INT);
            put(CharType.CHAR_SIGN, State.STATE_INT_SIGN);
        }};
        transfer.put(State.STATE_INITIAL, initialMap);
        Map<CharType, State> intSignMap = new HashMap<CharType, State>() {
  
  {
            put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
            put(CharType.CHAR_POINT, State.STATE_POINT_WITHOUT_INT);
        }};
        transfer.put(State.STATE_INT_SIGN, intSignMap);
        Map<CharType, State> integerMap = new HashMap<CharType, State>() {
  
  {
            put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
            put(CharType.CHAR_EXP, State.STATE_EXP);
            put(CharType.CHAR_POINT, State.STATE_POINT);
            put(CharType.CHAR_SPACE, State.STATE_END);
        }};
        transfer.put(State.STATE_INTEGER, integerMap);
        Map<CharType, State> pointMap = new HashMap<CharType, State>() {
  
  {
            put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
            put(CharType.CHAR_EXP, State.STATE_EXP);
            put(CharType.CHAR_SPACE, State.STATE_END);
        }};
        transfer.put(State.STATE_POINT, pointMap);
        Map<CharType, State> pointWithoutIntMap = new HashMap<CharType, State>() {
  
  {
            put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
        }};
        transfer.put(State.STATE_POINT_WITHOUT_INT, pointWithoutIntMap);
        Map<CharType, State> fractionMap = new HashMap<CharType, State>() {
  
  {
            put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
            put(CharType.CHAR_EXP, State.STATE_EXP);
            put(CharType.CHAR_SPACE, State.STATE_END);
        }};
        transfer.put(State.STATE_FRACTION, fractionMap);
        Map<CharType, State> expMap = new HashMap<CharType, State>() {
  
  {
            put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
            put(CharType.CHAR_SIGN, State.STATE_EXP_SIGN);
        }};
        transfer.put(State.STATE_EXP, expMap);
        Map<CharType, State> expSignMap = new HashMap<CharType, State>() {
  
  {
            put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
        }};
        transfer.put(State.STATE_EXP_SIGN, expSignMap);
        Map<CharType, State> expNumberMap = new HashMap<CharType, State>() {
  
  {
            put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
            put(CharType.CHAR_SPACE, State.STATE_END);
        }};
        transfer.put(State.STATE_EXP_NUMBER, expNumberMap);
        Map<CharType, State> endMap = new HashMap<CharType, State>() {
  
  {
            put(CharType.CHAR_SPACE, State.STATE_END);
        }};
        transfer.put(State.STATE_END, endMap);

        int length = s.length();
        State state = State.STATE_INITIAL;

        for (int i = 0; i < length; i++) {
            CharType type = toCharType(s.charAt(i));
            if (!transfer.get(state).containsKey(type)) {
                return false;
            } else {
                state = transfer.get(state).get(type);
            }
        }
        return state == State.STATE_INTEGER || state == State.STATE_POINT || state == State.STATE_FRACTION || state == State.STATE_EXP_NUMBER || state == State.STATE_END;
    }

    public CharType toCharType(char ch) {
        if (ch >= '0' && ch <= '9') {
            return CharType.CHAR_NUMBER;
        } else if (ch == 'e' || ch == 'E') {
            return CharType.CHAR_EXP;
        } else if (ch == '.') {
            return CharType.CHAR_POINT;
        } else if (ch == '+' || ch == '-') {
            return CharType.CHAR_SIGN;
        } else if (ch == ' ') {
            return CharType.CHAR_SPACE;
        } else {
            return CharType.CHAR_ILLEGAL;
        }
    }

    enum State {
        STATE_INITIAL,
        STATE_INT_SIGN,
        STATE_INTEGER,
        STATE_POINT,
        STATE_POINT_WITHOUT_INT,
        STATE_FRACTION,
        STATE_EXP,
        STATE_EXP_SIGN,
        STATE_EXP_NUMBER,
        STATE_END
    }

    enum CharType {
        CHAR_NUMBER,
        CHAR_EXP,
        CHAR_POINT,
        CHAR_SIGN,
        CHAR_SPACE,
        CHAR_ILLEGAL
    }
}

4. 把字符串转换成整数(中等)

4.1. 题目描述

请问什么是有符号整数?

有符号整数,就是int,因为有正负之分,所以16位的第一位表示正负,0为正,1为负所以能表示的范围

是-32768~+32767(-2e15~2e15-1)而无符号整数,就是定义为unsigned int,因为第一位不用代

表正负了,没有符号,全是正的啊,所以16位全为有效位,所以范围是0~65535(0~2e16-1)

4.2. 解题思路

class Solution {

    public int strToInt(String str) {
        char[] c = str.trim().toCharArray();
        if(c.length == 0) return 0;
        int res = 0, bndry = Integer.MAX_VALUE / 10;
        int i = 1, sign = 1;
        if(c[0] == '-') sign = -1;
        else if(c[0] != '+') i = 0;
        for(int j = i; j < c.length; j++) {
            if(c[j] < '0' || c[j] > '9') break;
            if(res > bndry || res == bndry && c[j] > '7') return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            res = res * 10 + (c[j] - '0');
        }
        return sign * res;
    }
}

若不使用trim() / strip() 方法,而从头开始遍历字符串,则可以将空间复杂度降低至 O(1) ,代码如下:

class Solution {
    public int strToInt(String str) {
        int res = 0, bndry = Integer.MAX_VALUE / 10;
        int i = 0, sign = 1, length = str.length();
        if(length == 0) return 0;
        while(str.charAt(i) == ' ')
            if(++i == length) return 0;
        if(str.charAt(i) == '-') sign = -1;
        if(str.charAt(i) == '-' || str.charAt(i) == '+') i++;
        for(int j = i; j < length; j++) {
            if(str.charAt(j) < '0' || str.charAt(j) > '9') break;
            if(res > bndry || res == bndry && str.charAt(j) > '7')
                return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            res = res * 10 + (str.charAt(j) - '0');
        }
        return sign * res;
    }

}

5. 交替合并字符串(简单)

5.1. 题目描述

5.2. 解题思路

方法一:双指针
class Solution {
    public String mergeAlternately(String word1, String word2) {
        int m = word1.length(), n = word2.length();
        int i = 0, j = 0;

        StringBuilder ans = new StringBuilder();
        while (i < m || j < n) {
            if (i < m) {
                ans.append(word1.charAt(i));
                ++i;
            }
            if (j < n) {
                ans.append(word2.charAt(j));
                ++j;
            }
        }
        return ans.toString();
    }
}

6. 字符串的最大公因子(简单)

6.1. 题目描述

什么是最大公因数 最大公因数的解释

1、最大公因数,也称为最大公约数、最大公因子,指两个或多个整数共有约数中最大的一个。

2、a,b的最大公约数记为(a,b),同样的,a,b,c的最大公约数记为(a,b,c),多个整数的最

大公约数也有同样的记号。

求最大公约数有多种方法,常见的有质因数分解法、短除法、辗转相除法、更相减损法。

与最大公约数相对应的概念是最小公倍数,a,b的最小公倍数记为[a,b]。

6.2. 解题思路

方法一:枚举
class Solution {
    // 字符串最大公因子
    public String gcdOfStrings(String str1, String str2) {
        // 获取两个字符串长度
        int len1 = str1.length(), len2 = str2.length();
        // 从长度小的开始枚举
        for (int i = Math.min(len1, len2); i >= 1; --i) {
            // 前缀串的长度必然是两个字符串长度的约数才能满足条件
            if (len1 % i == 0 && len2 % i == 0) {
                // 截取子串
                String X = str1.substring(0, i);
                // 检测子串是否能被两个字符串整除
                if (check(X, str1) && check(X, str2)) {
                    return X;
                }
            }
        }
        return "";
    }

    public boolean check(String t, String s) {
        int lenx = s.length() / t.length();
        StringBuffer ans = new StringBuffer();
        for (int i = 1; i <= lenx; ++i) {
            ans.append(t);
        }
        return ans.toString().equals(s);
    }
}
方法二:枚举优化
class Solution {
	// 字符串最大公因子
    public String gcdOfStrings(String str1, String str2) {
        // 获取两个字符串长度
        int len1 = str1.length(), len2 = str2.length();
        // 截取子串(截取到最大公因子)
        String T = str1.substring(0, gcd(len1, len2));
		// 检测子串是否能被两个字符串整除
        if (check(T, str1) && check(T, str2)) {
            return T;
        }
        return "";
    }

    public boolean check(String t, String s) {
        int lenx = s.length() / t.length();
        StringBuffer ans = new StringBuffer();
        for (int i = 1; i <= lenx; ++i) {
            ans.append(t);
        }
        return ans.toString().equals(s);
    }

    // 求最大公因子
    public int gcd(int a, int b) {
        int remainder = a % b;
        while (remainder != 0) {
            a = b;
            b = remainder;
            remainder = a % b;
        }
        return b;
    }
}
方法三:数学
class Solution {
	// 字符串最大公因子
    public String gcdOfStrings(String str1, String str2) {
        // 两个字符串相连不相等,直接返回空串
        if (!str1.concat(str2).equals(str2.concat(str1))) {
            return "";
        }
        // 
        return str1.substring(0, gcd(str1.length(), str2.length()));
    }
	// 截取子串(截取到最大公因子)
    public int gcd(int a, int b) {
        int remainder = a % b;
        while (remainder != 0) {
            a = b;
            b = remainder;
            remainder = a % b;
        }
        return b;
    }
}

7. 反转字符串中的元音字母(简单)

7.1. 题目描述

7.2. 解题思路

方法一:双指针
class Solution {
    public String reverseVowels(String s) {
        int n = s.length();
        char[] arr = s.toCharArray();
        int i = 0, j = n - 1;
        while (i < j) {
            while (i < n && !isVowel(arr[i])) {
                ++i;
            }
            while (j > 0 && !isVowel(arr[j])) {
                --j;
            }
            if (i < j) {
                swap(arr, i, j);
                ++i;
                --j;
            }
        }
        return new String(arr);
    }

    public boolean isVowel(char ch) {
        return "aeiouAEIOU".indexOf(ch) >= 0;
    }

    public void swap(char[] arr, int i, int j) {
        char temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

8. 递增的三元子序列(中等)

8.1. 题目描述

8.2. 解题思路

方法一:双向遍历
class Solution {
    public boolean increasingTriplet(int[] nums) {
        int n = nums.length;
        if (n < 3) {
            return false;
        }
        int[] leftMin = new int[n];
        leftMin[0] = nums[0];
        for (int i = 1; i < n; i++) {
            leftMin[i] = Math.min(leftMin[i - 1], nums[i]);
        }
        int[] rightMax = new int[n];
        rightMax[n - 1] = nums[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            rightMax[i] = Math.max(rightMax[i + 1], nums[i]);
        }
        for (int i = 1; i < n - 1; i++) {
            if (nums[i] > leftMin[i - 1] && nums[i] < rightMax[i + 1]) {
                return true;
            }
        }
        return false;
    }
}
方法二:贪心
class Solution {
    public boolean increasingTriplet(int[] nums) {
        int n = nums.length;
        if (n < 3) {
            return false;
        }
        int first = nums[0], second = Integer.MAX_VALUE;
        for (int i = 1; i < n; i++) {
            int num = nums[i];
            if (num > second) {
                return true;
            } else if (num > first) {
                second = num;
            } else {
                first = num;
            }
        }
        return false;
    }
}

9. 压缩字符串(中等)

9.1. 题目描述

9.2. 解题思路

方法一:双指针
class Solution {
    public int compress(char[] chars) {
        int n = chars.length;
        int write = 0, left = 0;
        for (int read = 0; read < n; read++) {
            if (read == n - 1 || chars[read] != chars[read + 1]) {
                chars[write++] = chars[read];
                int num = read - left + 1;
                if (num > 1) {
                    int anchor = write;
                    while (num > 0) {
                        chars[write++] = (char) (num % 10 + '0');
                        num /= 10;
                    }
                    reverse(chars, anchor, write - 1);
                }
                left = read + 1;
            }
        }
        return write;
    }

    public void reverse(char[] chars, int left, int right) {
        while (left < right) {
            char temp = chars[left];
            chars[left] = chars[right];
            chars[right] = temp;
            left++;
            right--;
        }
    }
}

10. 反转字符串中的单词(中等)

10.1. 题目描述

10.2. 解题思路

方法一:使用语言特性
class Solution {
    public String reverseWords(String s) {
        // 除去开头和末尾的空白字符
        s = s.trim();
        // 正则匹配连续的空白字符作为分隔符分割
        List<String> wordList = Arrays.asList(s.split("\\s+"));
        Collections.reverse(wordList);
        return String.join(" ", wordList);
    }
}
方法二:自行编写对应的函数
class Solution {
    public String reverseWords(String s) {
        StringBuilder sb = trimSpaces(s);

        // 翻转字符串
        reverse(sb, 0, sb.length() - 1);

        // 翻转每个单词
        reverseEachWord(sb);

        return sb.toString();
    }

    public StringBuilder trimSpaces(String s) {
        int left = 0, right = s.length() - 1;
        // 去掉字符串开头的空白字符
        while (left <= right && s.charAt(left) == ' ') {
            ++left;
        }

        // 去掉字符串末尾的空白字符
        while (left <= right && s.charAt(right) == ' ') {
            --right;
        }

        // 将字符串间多余的空白字符去除
        StringBuilder sb = new StringBuilder();
        while (left <= right) {
            char c = s.charAt(left);

            if (c != ' ') {
                sb.append(c);
            } else if (sb.charAt(sb.length() - 1) != ' ') {
                sb.append(c);
            }

            ++left;
        }
        return sb;
    }

    public void reverse(StringBuilder sb, int left, int right) {
        while (left < right) {
            char tmp = sb.charAt(left);
            sb.setCharAt(left++, sb.charAt(right));
            sb.setCharAt(right--, tmp);
        }
    }

    public void reverseEachWord(StringBuilder sb) {
        int n = sb.length();
        int start = 0, end = 0;

        while (start < n) {
            // 循环至单词的末尾
            while (end < n && sb.charAt(end) != ' ') {
                ++end;
            }
            // 翻转单词
            reverse(sb, start, end - 1);
            // 更新start,去找下一个单词
            start = end + 1;
            ++end;
        }
    }
}
方法三:双端队列
class Solution {
    public String reverseWords(String s) {
        int left = 0, right = s.length() - 1;
        // 去掉字符串开头的空白字符
        while (left <= right && s.charAt(left) == ' ') {
            ++left;
        }

        // 去掉字符串末尾的空白字符
        while (left <= right && s.charAt(right) == ' ') {
            --right;
        }

        Deque<String> d = new ArrayDeque<String>();
        StringBuilder word = new StringBuilder();
        
        while (left <= right) {
            char c = s.charAt(left);
            if ((word.length() != 0) && (c == ' ')) {
                // 将单词 push 到队列的头部
                d.offerFirst(word.toString());
                word.setLength(0);
            } else if (c != ' ') {
                word.append(c);
            }
            ++left;
        }
        d.offerFirst(word.toString());

        return String.join(" ", d);
    }
}

11. 字符串变形(简单)

11.1. 题目描述

11.2. 解题思路

方法一:双逆转(推荐使用)

Java代码实现:

import java.util.*;
public class Solution {
    public String trans(String s, int n) {
        if(n==0) 
            return s;
        StringBuffer res=new StringBuffer();
        for(int i = 0; i < n; i++){
            //大小写转换
            if(s.charAt(i) <= 'Z' && s.charAt(i) >= 'A')   
                res.append((char)(s.charAt(i) - 'A' + 'a'));
            else if(s.charAt(i) >= 'a' && s.charAt(i) <= 'z') 
                res.append((char)(s.charAt(i) - 'a' + 'A'));
            else 
                //空格直接复制
                res.append(s.charAt(i));  
        } 
        //翻转整个字符串
        res = res.reverse();
        for (int i = 0; i < n; i++){
            int j = i;
            //以空格为界,二次翻转
            while(j < n && res.charAt(j) != ' ')  
                j++;
            String temp = res.substring(i,j);
            StringBuffer buffer = new StringBuffer(temp);
            temp = buffer.reverse().toString();
            res.replace(i,j,temp);
            i = j;
        }
        return res.toString();
    }
}
方法二:分割字符串+栈(扩展思路)

java代码实现:

import java.util.*;
public class Solution {
    public String trans(String s, int n) {
        if(n==0) 
            return s;
        StringBuffer res=new StringBuffer();
        for (int i = 0; i < n; i++){
            //大小写转换
            if(s.charAt(i) <= 'Z' && s.charAt(i) >= 'A')   
                res.append((char)(s.charAt(i) - 'A' + 'a'));
            else if(s.charAt(i) >= 'a' && s.charAt(i) <= 'z') 
                res.append((char)(s.charAt(i) - 'a' + 'A'));
             else 
                //空格直接复制
                res.append((char)(s.charAt(i)));  
        }
        Stack<String> temp=new Stack<String>();
        for (int i = 0; i < n; i++){
            int j = i;
            //以空格为界,分割单词
            while(j < n && res.charAt(j) != ' ')  
                j++;
            //单词进栈
            temp.push((String)(res.substring(i, j)));  
            i = j;
        }
        //排除结尾空格的特殊情况
        if(s.charAt(n - 1) == ' ')  
            res = new StringBuffer(" ");
        else
            res = new StringBuffer();
        //栈遵循先进后厨,单词顺序是反的
        while(!temp.empty()){   
            res.append(temp.peek());
            temp.pop();
            if(!temp.empty())
                res.append(" ");
        }
        return res.toString();
    }
}

12. 最长公共前缀

12.1. 题目描述

12.2. 解题思路

方法一:遍历查找(推荐使用)

Java代码实现:

import java.util.*;
public class Solution {
    public String longestCommonPrefix (String[] strs) {
        int n = strs.length;
        //空字符串数组
        if(n == 0) 
            return "";
        //遍历第一个字符串的长度
        for(int i = 0; i < strs[0].length(); i++){ 
            char temp = strs[0].charAt(i); 
            //遍历后续的字符串
            for(int j = 1; j < n; j++) 
                //比较每个字符串该位置是否和第一个相同
                if(i == strs[j].length() || strs[j].charAt(i) != temp) 
                    //不相同则结束
                    return strs[0].substring(0, i); 
        }
        //后续字符串有整个字一个字符串的前缀
        return strs[0]; 
    }
}

13. 验证IP地址

13.1. 题目描述

13.2. 解题思路

方法一:分割字符串比较法(推荐使用)

java代码实现:

import java.util.*;
public class Solution {
    boolean isIPv4 (String IP) {
        if(IP.indexOf('.') == -1){
            return false;
        }
        String[] s = IP.split("\\.");  
        //IPv4必定为4组
        if(s.length != 4)  
            return false;
        for(int i = 0; i < s.length; i++){
            //不可缺省,有一个分割为零,说明两个点相连
            if(s[i].length() == 0)  
                return false;
            //比较数字位数及不为零时不能有前缀零
            if(s[i].length() < 0 || s[i].length() > 3 || (s[i].charAt(0)=='0' && s[i].length() != 1))  
                return false;
            int num = 0;
            //遍历每个分割字符串,必须为数字
            for(int j = 0; j < s[i].length(); j++){  
                char c = s[i].charAt(j);
                if (c < '0' || c > '9') 
                    return false;
            //转化为数字比较,0-255之间
            num = num * 10 + (int)(c - '0');  
            if(num < 0 || num > 255) 
                return false;
            }
        }    
        return true;
    }
    boolean isIPv6 (String IP) {
        if (IP.indexOf(':') == -1) {
            return false;
        }
        String[] s = IP.split(":",-1);
        //IPv6必定为8组
        if(s.length != 8){  
            return false;
        }
        for(int i = 0; i < s.length; i++){ 
            //每个分割不能缺省,不能超过4位
            if(s[i].length() == 0 || s[i].length() > 4){ 
                return false; 
            }
            for(int j = 0; j < s[i].length(); j++){
                //不能出现a-fA-F以外的大小写字符
                char c = s[i].charAt(j);
                boolean expr = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ;
                if(!expr){
                    return false;
                }
            }
        }
        return true;
    }
    String solve(String IP) {
        if(isIPv4(IP))
            return "IPv4";
        else if(isIPv6(IP))
            return "IPv6";
        return "Neither";
    }
}
方法二:正则表达式(扩展思路)

Java代码实现:

import java.util.regex.Pattern;
public class Solution {
    String solve(String IP) {
        //正则表达式限制0-255 且没有前缀0 四组齐全
        String ipv4="(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])";
        Pattern ipv4_pattern = Pattern.compile(ipv4);
        //正则表达式限制出现8组,0-9a-fA-F的数,个数必须是1-4个
        String ipv6="([0-9a-fA-F]{1,4}\\:){7}[0-9a-fA-F]{1,4}";
        Pattern ipv6_pattern = Pattern.compile(ipv6);
        //调用正则匹配函数
        if (ipv4_pattern.matcher(IP).matches()) 
            return "IPv4";
        else if (ipv6_pattern.matcher(IP).matches()) 
            return "IPv6";
        else return "Neither";
    }
}

14. 大数加法

14.1. 题目描述

14.2. 解题思路

方法一:模拟法(建议使用)

Java代码实现:

import java.util.*;
public class Solution {
    public String solve (String s, String t) {
         //若是其中一个为空,返回另一个
        if(s.length()<=0)
            return t;
        if(t.length()<=0)
            return s;
        //让s为较长的,t为较短的
        if(s.length() < t.length()){ 
            String temp = s;
            s = t;
            t = temp;
        }
        int carry = 0; //进位标志
        char[] res = new char[s.length()];
        //从后往前遍历较长的字符串
        for(int i = s.length() - 1; i >= 0; i--){ 
            //转数字加上进位
            int temp = s.charAt(i) - '0' + carry; 
            //转较短的字符串相应的从后往前的下标
            int j = i - s.length() + t.length(); 
            //如果较短字符串还有
            if(j >= 0) 
                //转数组相加
                temp += t.charAt(j) - '0'; 
            //取进位
            carry = temp / 10; 
            //去十位
            temp = temp % 10; 
            //修改结果
            res[i] = (char)(temp + '0'); 
        }
        String output = String.valueOf(res);
        //最后的进位
        if(carry == 1) 
            output = '1' + output;
        return output;
    }
}

15. 无重复字符的最长子串

15.1. 题目描述

15.2. 解题思路

方法一:滑动窗口
class Solution {
    public int lengthOfLongestSubstring(String s) {
        // 哈希集合,记录每个字符是否出现过
        Set<Character> occ = new HashSet<Character>();
        int n = s.length();
        // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        int rk = -1, ans = 0;
        for (int i = 0; i < n; ++i) {
            if (i != 0) {
                // 左指针向右移动一格,移除一个字符
                occ.remove(s.charAt(i - 1));
            }
            while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
                // 不断地移动右指针
                occ.add(s.charAt(rk + 1));
                ++rk;
            }
            // 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = Math.max(ans, rk - i + 1);
        }
        return ans;
    }
}

16. 找到字符串中所有字母异位词(中等)

16.1. 题目描述

16.2. 解题思路

方法一:滑动窗口
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();

        if (sLen < pLen) {
            return new ArrayList<Integer>();
        }

        List<Integer> ans = new ArrayList<Integer>();
        int[] sCount = new int[26];
        int[] pCount = new int[26];
        for (int i = 0; i < pLen; ++i) {
            ++sCount[s.charAt(i) - 'a'];
            ++pCount[p.charAt(i) - 'a'];
        }

        if (Arrays.equals(sCount, pCount)) {
            ans.add(0);
        }

        for (int i = 0; i < sLen - pLen; ++i) {
            --sCount[s.charAt(i) - 'a'];
            ++sCount[s.charAt(i + pLen) - 'a'];

            if (Arrays.equals(sCount, pCount)) {
                ans.add(i + 1);
            }
        }

        return ans;
    }
}
方法二:优化的滑动窗口
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        int sLen = s.length(), pLen = p.length();

        if (sLen < pLen) {
            return new ArrayList<Integer>();
        }

        List<Integer> ans = new ArrayList<Integer>();
        int[] count = new int[26];
        for (int i = 0; i < pLen; ++i) {
            ++count[s.charAt(i) - 'a'];
            --count[p.charAt(i) - 'a'];
        }

        int differ = 0;
        for (int j = 0; j < 26; ++j) {
            if (count[j] != 0) {
                ++differ;
            }
        }

        if (differ == 0) {
            ans.add(0);
        }

        for (int i = 0; i < sLen - pLen; ++i) {
            if (count[s.charAt(i) - 'a'] == 1) {  // 窗口中字母 s[i] 的数量与字符串 p 中的数量从不同变得相同
                --differ;
            } else if (count[s.charAt(i) - 'a'] == 0) {  // 窗口中字母 s[i] 的数量与字符串 p 中的数量从相同变得不同
                ++differ;
            }
            --count[s.charAt(i) - 'a'];

            if (count[s.charAt(i + pLen) - 'a'] == -1) {  // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从不同变得相同
                --differ;
            } else if (count[s.charAt(i + pLen) - 'a'] == 0) {  // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从相同变得不同
                ++differ;
            }
            ++count[s.charAt(i + pLen) - 'a'];
            
            if (differ == 0) {
                ans.add(i + 1);
            }
        }

        return ans;
    }
}

17. 定长子串中元音的最大数目(中等)

17.1. 题目描述

17.2. 解题思路

方法一:滑动窗口
class Solution {
    public int maxVowels(String s, int k) {
        int n = s.length();
        int vowel_count = 0;
        for (int i = 0; i < k; ++i) {
            vowel_count += isVowel(s.charAt(i));
        }
        int ans = vowel_count;
        for (int i = k; i < n; ++i) {
            vowel_count += isVowel(s.charAt(i)) - isVowel(s.charAt(i - k));
            ans = Math.max(ans, vowel_count);
        }
        return ans;
    }

    public int isVowel(char ch) {
        return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u' ? 1 : 0;
    }
}

18. 判断是否为回文字符串

18.1. 题目描述

18.2. 解题思路

方法一:首尾依次比较法(推荐使用)

Java代码实现:

import java.util.*;
public class Solution {
    public boolean judge (String str) {
        //首指针
        int left = 0; 
        //尾指针
        int right = str.length() - 1;
        //首尾往中间靠  
        while(left < right){  
            //比较前后是否相同
            if(str.charAt(left) != str.charAt(right)) 
                return false;
            left++;
            right--;
        }
        return true;
    }
}
方法二:反转字符串比较法(扩展思路)
import java.util.*;
public class Solution {
    public boolean judge (String str) {
        StringBuffer temp = new StringBuffer(str);
        //反转字符串
        String s = temp.reverse().toString();
        //比较字符串是否相等
        if(s.equals(str))
            return true;
        return false;
    }
}

19. 最小覆盖子串(较难)

19.1. 题目描述

19.2. 解题思路

方法一:哈希表匹配(推荐使用)

Java代码实现:

import java.util.*;
public class Solution {
    //检查是否有小于0的
    boolean check(int[] hash) { 
        for (int i = 0; i < hash.length; i++) {
            if (hash[i] < 0)
                return false;
        }
        return true;
    };
    
    public String minWindow (String S, String T) {
        int cnt = S.length() + 1;
        //记录目标字符串T的字符个数
        int[] hash = new int[128]; 
        for(int i = 0; i < T.length(); i++)
        //初始化哈希表都为负数,找的时候再加为正
            hash[T.charAt(i)] -= 1; 
        int slow = 0, fast = 0;  
        //记录左右区间
        int left = -1, right = -1;  
        for(; fast < S.length(); fast++){
            char c = S.charAt(fast);
            //目标字符匹配+1
            hash[c]++;
            //没有小于0的说明都覆盖了,缩小窗口
            while(check(hash)){  
                //取最优解
                if(cnt > fast - slow + 1){ 
                    cnt = fast - slow + 1;  
                    left = slow;
                    right = fast;
                }
                c = S.charAt(slow);
                //缩小窗口的时候减1
                hash[c]--; 
                //窗口缩小
                slow++;      
            }
        }
        //找不到的情况
        if(left == -1)     
            return "";
        return S.substring(left, right + 1);
    }
}

20. 反转字符串(入门)

20.1. 题目描述

20.2. 解题思路

解法一
import java.util.*;
public class Solution {
    public String solve (String str) {
        char[] ans = str.toCharArray();
        int len = str.length();
        for(int i = 0 ; i < len ;i++)
        {
                ans[i] = str.charAt(len-1-i);
        }
        return new String(ans);
    }
}
解法二
import java.util.*;
public class Solution {
    public String solve (String str) {
        char[] cstr = str.toCharArray();
        int len = str.length();
        for(int i = 0 ; i < len/2 ;i++)
        {
                char t = cstr[i];
                cstr[i] = cstr[len-1-i];
                cstr[len-1-i]=t;
        }
        return new String(cstr);
    }
}
解法三

直接调用库函数

import java.util.*;
public class Solution {
    public String solve (String str) {
        StringBuffer sb =new StringBuffer(str);//此方法针对的是io流,不能针对字符串。
        return sb.reverse().toString();
    }
}
相关推荐
_Itachi__9 分钟前
LeetCode 热题100 21. 合并两个有序链表
算法·leetcode·链表
橘猫云计算机设计10 分钟前
基于微信小程序的疫情互助平台(源码+lw+部署文档+讲解),源码可白嫖!
java·大数据·开发语言·spring boot·微信小程序·小程序·汽车
小妖66614 分钟前
css3的transform:tanslateZ没有效果
前端·css·css3
m0_6724496016 分钟前
使用Java操作Excel
java·python·excel
customer0828 分钟前
【开源免费】基于SpringBoot+Vue.JS医院药品管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
见青..36 分钟前
[BUUCTF]web--wp(持续更新中)
前端·web安全·网络安全
无世世1 小时前
【Java从入门到起飞】面向对象编程(基础)
java·开发语言
二川bro1 小时前
前端模块化管理深度解析:从混沌到秩序的全链路实践指南
前端
Code哈哈笑1 小时前
【Java EE】JavaEE导读,探寻 JavaEE:解锁企业级开发的璀璨密码与进阶指南
java·java-ee
JosieBook1 小时前
【前端】在WebStorm中安装Node.js与nvm与npm的详细过程
前端·node.js·webstorm