算法训练营day08 | 字符串part02

67 阅读4分钟

151.翻转字符串里的单词

题目链接

第一次做主要想要快速pass题目,代码如下。

class Solution {
    public String reverseWords(String s) {
        s = s.replaceAll("\\s{2,}", " ").trim();
        String[] strings = s.split(" ");
        int left = 0;
        int right = strings.length -1;
        while(left < right){
            
            String temp = strings[left];
            strings[left] = strings[right];
            strings[right] = temp;

            left++;
            right--;
        }

        String res = String.join(" ", strings);
        return res.strip();
    }
}

随想录

文章链接 随想录提了一点难度:不要使用辅助空间,空间复杂度要求为O(1)。 解题思路如下:

  • 移除多余空格 -> 使用双指针法来去移除空格,最后resize(重新设置)一下字符串的大小,就可以做到O(n)的时间复杂度。
  • 将整个字符串反转
  • 将每个单词反转
class Solution {
   /**
     * 不使用Java内置方法实现
     * <p>
     * 1.去除首尾以及中间多余空格
     * 2.反转整个字符串
     * 3.反转各个单词
     */
    public String reverseWords(String s) {
        // System.out.println("ReverseWords.reverseWords2() called with: s = [" + s + "]");
        // 1.去除首尾以及中间多余空格
        StringBuilder sb = removeSpace(s);
        // 2.反转整个字符串
        reverseString(sb, 0, sb.length() - 1);
        // 3.反转各个单词
        reverseEachWord(sb);
        return sb.toString();
    }

    private StringBuilder removeSpace(String s) {
        // System.out.println("ReverseWords.removeSpace() called with: s = [" + s + "]");
        int start = 0;
        int end = s.length() - 1;
        while (s.charAt(start) == ' ') start++;
        while (s.charAt(end) == ' ') end--;
        StringBuilder sb = new StringBuilder();
        while (start <= end) {
            char c = s.charAt(start);
            if (c != ' ' || sb.charAt(sb.length() - 1) != ' ') {
                sb.append(c);
            }
            start++;
        }
        // System.out.println("ReverseWords.removeSpace returned: sb = [" + sb + "]");
        return sb;
    }

    /**
     * 反转字符串指定区间[start, end]的字符
     */
    public void reverseString(StringBuilder sb, int start, int end) {
        // System.out.println("ReverseWords.reverseString() called with: sb = [" + sb + "], start = [" + start + "], end = [" + end + "]");
        while (start < end) {
            char temp = sb.charAt(start);
            sb.setCharAt(start, sb.charAt(end));
            sb.setCharAt(end, temp);
            start++;
            end--;
        }
        // System.out.println("ReverseWords.reverseString returned: sb = [" + sb + "]");
    }

    private void reverseEachWord(StringBuilder sb) {
        int start = 0;
        int end = 1;
        int n = sb.length();
        while (start < n) {
            while (end < n && sb.charAt(end) != ' ') {
                end++;
            }
            reverseString(sb, start, end - 1);
            start = end + 1;
            end = start + 1;
        }
    }
}

卡码网:55.右旋转字符串

题目链接

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int k = Integer.parseInt(in.nextLine());
        String s = in.nextLine();

        int len = s.length();  //获取字符串长度
        char[] chars = s.toCharArray();
        char[] res = new char[len];
        for(int i = len -k;i<len;i++){
            res[i-(len-k)] = chars[i];
        }
        for(int i=0;i < len-k;i++){
            res[i+k] = chars[i];
        }
        
        System.out.println(res);

    }
    
}

随想录

文章链接

不能申请额外空间,只能在本串上操作。 (java除了为了在string上做操作开辟的空间除外)。 使用整体反转+局部反转就可以实现反转单词顺序的目的。思路就是 通过 整体倒叙,把两段子串顺序颠倒,两个段子串里的的字符在倒叙一把,负负得正,这样就不影响子串里面字符的顺序了。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = Integer.parseInt(in.nextLine());
        String s = in.nextLine();

        int len = s.length();  //获取字符串长度
        char[] chars = s.toCharArray();
        reverseString(chars, 0, len - 1);  //反转整个字符串
        reverseString(chars, 0, n - 1);  //反转前一段字符串,此时的字符串首尾尾是0,n - 1
        reverseString(chars, n, len - 1);  //反转后一段字符串,此时的字符串首尾尾是n,len - 1
        
        System.out.println(chars);

    }
    
    public static void reverseString(char[] ch, int start, int end) {
        //异或法反转字符串,参照题目 344.反转字符串的解释
        while (start < end) {
            ch[start] ^= ch[end];
            ch[end] ^= ch[start];
            ch[start] ^= ch[end];
            start++;
            end--;
        }
    }
}

28. 实现 strStr()

题目链接

class Solution {
    public int strStr(String haystack, String needle) {
        int slow = 0;
        int fast = 0;

        while((fast < haystack.length()) && (slow < needle.length())){
            if(haystack.charAt(fast) == needle.charAt(slow)){
                fast++;
                slow++;
            }else if(slow != 0){
                fast = fast - slow + 1;
                slow = 0;
            }else{
                fast++;
            }
        }

        if(slow == needle.length()){
            return fast - needle.length();
        }

        return -1;
    }
}

随想录

文章链接 KMP 经典题目 KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。

class Solution {
    /**
	牺牲空间,换取最直白的暴力法
        时间复杂度 O(n * m)
        空间 O(n + m)
     */
    public int strStr(String haystack, String needle) {
        // 获取 haystack 和 needle 的长度
        int n = haystack.length(), m = needle.length();
        // 将字符串转换为字符数组,方便索引操作
        char[] s = haystack.toCharArray(), p = needle.toCharArray();

        // 遍历 haystack 字符串
        for (int i = 0; i < n - m + 1; i++) {
            // 初始化匹配的指针
            int a = i, b = 0;
            // 循环检查 needle 是否在当前位置开始匹配
            while (b < m && s[a] == p[b]) {
                // 如果当前字符匹配,则移动指针
                a++;
                b++;
            }
            // 如果 b 等于 m,说明 needle 已经完全匹配,返回当前位置 i
            if (b == m) return i;
        }

        // 如果遍历完毕仍未找到匹配的子串,则返回 -1
        return -1;
    }
}

459.重复的子字符串

题目链接

随想录

文章链接

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        if (s.equals("")) return false;

        int len = s.length();
        // 原串加个空格(哨兵),使下标从1开始,这样j从0开始,也不用初始化了
        s = " " + s;
        char[] chars = s.toCharArray();
        int[] next = new int[len + 1];

        // 构造 next 数组过程,j从0开始(空格),i从2开始
        for (int i = 2, j = 0; i <= len; i++) {
            // 匹配不成功,j回到前一位置 next 数组所对应的值
            while (j > 0 && chars[i] != chars[j + 1]) j = next[j];
            // 匹配成功,j往后移
            if (chars[i] == chars[j + 1]) j++;
            // 更新 next 数组的值
            next[i] = j;
        }

        // 最后判断是否是重复的子字符串,这里 next[len] 即代表next数组末尾的值
        if (next[len] > 0 && len % (len - next[len]) == 0) {
            return true;
        }
        return false;
    }
}

字符串总结

文章链接

双指针回顾

文章链接