随想录训练营8/60 | LC 28. 实现 strStr();LC 459.重复的子字符串

LC 28. 实现 strStr()

题目链接:LC 28. 实现 strStr()
思路KMP算法主要是理解KMP算法的思想,核心是前缀表(next数组)的构造。
通过暴力算法找字符串中是否含有指定字串复杂度过高,可以通过观察指定字串有什么规律,来减少复杂度(剪枝)。
那么如何构造前缀表呢?(字串的字串前后缀)
前缀表的长度等于模式串的长度,前缀表第i位等于从字串开始到i的字串的相同前后缀。
随想录训练营8/60 | LC 28. 实现 strStr();LC 459.重复的子字符串_第1张图片
也有对next数组全部减一的操作,这种情况下,是回退到与之前匹配的位置。代码细节有不同。

class Solution {
public:
    //得到next数组
    void getNext(int* next, const string& s){
        int l = 0;//l既是下标,又是最长相同前后缀的长度
        next[l] = l;//数组第0位初始化为0
        for(int r=1; r<s.size(); ++r){//用r遍历字符串s,从1开始,第0位已经确定
            while(l!=0 && s[l]!=s[r]){//当第l位与第r位不相同时,进行回退
            //回退只改变j的值,直到找到相同的前后缀,才停止回退
            //l等于0的时候跳出是为了防止数组下标越界
                l = next[l-1];
            }
            if(s[l] == s[r])++l;//这一步的意义为,当l为0时,若第l位的char与第r位的char相同,则l为1(next[r]为1)
            next[r] = l;
        }
    }
    //返回的是下标
    //先创建并构造前缀表
    int strStr(string haystack, string needle) {
        int next[needle.size()];//创建前缀表
        getNext(next, needle);//构造前缀表
        int j = 0;//回退到哪个char的下标
        for(int i=0; i<haystack.size(); i++){
            //若发生不匹配,则进行回退,直到匹配
            while(j!=0 && needle[j]!=haystack[i]){
                j = next[j-1];
            }
            //当跳出循环时,要不j=0;要不needle[j]==haystack[i]
            //当相等的时候,j向后走一位
            if(needle[j] == haystack[i])j++;
            //当不等的时候,从needle[0]与haystack[i+1]开始比较
            //当发现最后一位也匹配上时,跳出
            if(j == needle.size()) return i-(needle.size()-1);
        }
        return -1;
    }
};

LC 459.重复的子字符串

题目链接:LC 459.重复的子字符串
思路KMP若有最小重复字串,则其为最长相同前后缀所剩下的部分。如果字符串由重复字串构成,那么它的next数组最后一位就是最长前后缀的长度。若不是由重复字串构成,那么它的最后一位不是最大值。不过我们不用通过判断最后一位是否是最大值来判断是否可重复(这是必要不充分条件)。而是通过next数组最后一位来计算重复字串的长度,长度若能被整个字符串整除,就返回true,否则就false。
next数组升是慢慢往上升的,降可能是突然降的。

你可能感兴趣的:(leetcode)