代码随想录刷题day 8 | ● 151.翻转字符串里的单词 卡码网:55.右旋转字符串 28. 实现 strStr() 459.重复的子字符串

[151. 反转字符串中的单词](https://leetcode.cn/problems/reverse-string/)

class Solution {
    public String reverseWords(String s) {
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < s.length(); i++){
            if(s.charAt(i) == ' ' && sb.length() == 0) continue;  //跳过字符串首的空格
            if(s.charAt(i) != ' ' || s.charAt(i) == ' ' && s.charAt(i - 1) != ' ') 
                sb.append(s.charAt(i));  //保留单词间第一个空格以及单词内容
        }
        if(sb.charAt(sb.length()-1) == ' ') sb.deleteCharAt(sb.length()-1); //如果串末尾有空格,经过上面处理最多只剩一个,删除即可
        char[] chr = sb.toString().toCharArray();
        reverse(chr, 0, chr.length - 1);
        int left = 0;  //left代表单词的起始位置,right为末尾的下一个位置
        while(left < chr.length){
            int right = left;
            while(right < chr.length && chr[right] != ' ') right++; //找单词末尾的下一个位置
            reverse(chr, left, right-1);
            left = right + 1;
        }
        return new String(chr);
    }

    public void reverse(char[] chr, int left, int right){//前闭后闭
        while(left < right){
            char tmp = chr[left];
            chr[left] = chr[right];
            chr[right] = tmp;
            left++; right--;
        }
    } 
}

28. 找出字符串中第一个匹配项的下标

有一点理解,但理解的不多。next数组是前缀表统一减一。

最长公共前后缀

文章中字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。

后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。

首先先把握住最长公共前后缀的概念,这里可以看代码随想录的讲解。其次这里有几个变量的含义一定要把握住,首先就是j,代表了前缀的末尾,刚开始时还没有开始匹配,因此起始值为-1;i代表后缀的末尾,因为起始的时候只有第一个字符,公共串的末尾就是1。

接下来需要理解next数组的含义,next数组就是前缀表,但在用的时候还可以这么理解,他代表了公共前后缀的长度,也就意味着,如果这个字符的下一个没有匹配上,我们可以直接跳过前后公共的部分,从前缀的下一个位置开始匹配(注意理解这句话,比如abcabe在e处没有匹配上,但是ab是公共的,因此我们可以直接从c处进行匹配)。

计算next的部分需要处理两种情况,先是没有匹配上,那么说明当前从0->i这个字串最后一个字符匹配不上,但有可能出现一个更短的子串能匹配上,因此是j = next[j], 这里举个例子(aeac aeae)显然在匹配到最后一个e的时候,公共子串并不能扩展,即公共前后缀长度小于4,但也不能一下就等于0,我们可以看出,还是有公共部分ae,这是用j = next[j]就能把这个公共部分求出来。但是也是意会了,写的不是很清楚。

匹配上就非常简单了,j++即可,代表公共前后缀是前一个字符处值加一。填上next值即可。

使用next数组进行匹配的过程跟计算next数组是差不多的,区别就是,这时候i代表要去查找的串的当前匹配的位置,j+1代表将要在模式串中匹配的位置。j+1从零开始,因此j的初值就是-1。而i因为是要查找的串的索引,因此是从0开始一直到最后的位置,里面的内容除了匹配完成后返回值部分不同,其余完全一致。当完全匹配上时,很显然,j指向模式串的末尾,即motif.length()-1,i代表在查找串中,与j位置相对应的那个字符,因此,查找到的位置应该用i - j。

class Solution {
    public int strStr(String haystack, String needle) {
        if(needle.length() > haystack.length()) return -1;
        int[] next = new int[needle.length()];
        getNext(next, needle);
        int j = -1;
        for(int i = 0; i < haystack.length(); i++){
            while(haystack.charAt(i) != needle.charAt(j+1) && j >= 0) j = next[j];
            if(haystack.charAt(i) == needle.charAt(j+1)) j++;
            if(j == needle.length() - 1) return i - j;
        }
        return -1;
    }

    public void getNext(int[] next, String s){
        int j = -1;
        next[0] = j;
        for(int i = 1; i < s.length(); i++){
            while(s.charAt(j+1) != s.charAt(i) && j >= 0) j = next[j];
            if(s.charAt(j+1) == s.charAt(i)) j++;
            next[i] = j;
        }
    }
}

459. 重复的子字符串

暴力法就没写了,这里写了个用kmp来做的,但是也是照着人家的思路实现了而已。要我自己想的话肯定是想不出来的。

class Solution {
    public boolean repeatedSubstringPattern(String s) {
        if(s.length() == 1) return false;
        int[] next = new int[s.length()];
        getNext(next, s);
        int len = next[next.length - 1] + 1;
        int len_s = s.length();
        return (len >= len_s / 2) && (len_s % (len_s - len) == 0);
    }
    public void getNext(int[] next, String s){
        int j = -1;
        next[0] = j;  //这句千万别忘了
        for(int i = 1; i < s.length(); i++){
            while(s.charAt(i) != s.charAt(j+1) && j >= 0) j = next[j];
            if(s.charAt(i) == s.charAt(j+1)) j++;
            next[i] = j;
        }
    }
}
posted @ 2024-07-11 22:43  12点不睡觉还想干啥?  阅读(16)  评论(0)    收藏  举报