代码随想录算法训练营第七天|KMP算法-28. 实现 strStr()、459.重复的子字符串、字符串总结、双指针回顾

KMP算法

能够记住已经比较过的和模式串相等的部分,然后就着它继续比较,就好像是有记忆的比较。

概念

  • 前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串
  • 后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串
  • next数组存储的是当前子串(next[i]即对应前i个字符组成的字符串)的最长前缀(存在相同后缀)的下一个位置的下标,用于跳转到(有记忆地比较)字符的下一个。
    e.g. issi next=1 -> 前缀”i“和与之相等的后缀”i“是一对最长的相等前后缀,next等于其长度,即为1(注意前缀“is”和后缀“si”并不相同)
    e.g. ababab next=4->前缀“abab”和后缀“abab”

求next数组

next数组有三种表示形式,其中对应了不同的跳转方式:

  1. 最原始的:j=next[j-1] 
  2. 整体减一:j=next[j-1]+1
  3. 整体右移:j=next[j]

对于第一种next数组,求法如下(其他,大差不差)——

通过保证前缀尾部和后缀尾部一直相同,进而试图扩张前缀长度,如果允许扩张,就更新next数组,反之就苟着,下面是第二种的图,凑活看。

初始化

i定义为后缀尾部,j定义为前缀尾部;j显然从0开始,而前缀起码要有两个字符才有意义,于是i初始为1,因为可能用到next数组的第一个值,所以也需要对next[0]初始化,为0。

前后缀不一致

如果字符不一致,那就需要跳转j一直到一致或者j=0为止,只有这样才能保证前缀和后缀是始终相等的。

前后缀一致

那就j++,继续比较下一个字符。

更新

注意无论前后缀是否一致,都是要更新这里的next值的,如果恰好一致,那值就会是j,如果不一致那就会统一为0。

还要注意这三个部分的顺序不能颠倒,因为执行了while循环跳转j的有可能真让他跳转到了与i对应的值相等的点,这时候还是要执行j++的,最后才能更新具体的next值。

void getNext(int* next, const string& s) {
        int j = 0;
        next[0] = 0;
        for(int i = 1; i < s.size(); i++) {
            while (j > 0 && s[i] != s[j]) {
                j = next[j - 1];
            }
            if (s[i] == s[j]) {
                j++;
            }
            next[i] = j;
        }
    }

28. 实现 strStr()  

因为KMP算法很难,大家别奢求 一次就把kmp全理解了,大家刚学KMP一定会有各种各样的疑问,先留着,别期望立刻啃明白,第一遍了解大概思路,二刷的时候,再看KMP会 好懂很多。或者说大家可以放弃一刷可以不看KMP,今天来回顾一下之前的算法题目就可以。

因为大家 算法能力还没到,细扣 很难的算法,会把自己绕进去,就算别人给解释,只会激发出更多的问题和疑惑。所以大家先了解大体过程,知道这么回事, 等自己有 算法基础和思维了,在看多看几遍视频,慢慢就理解了。

题目链接/文章讲解/视频讲解:代码随想录

这是KMP算法的一个基础应用。文章里面主要介绍了next数组是如何得到的,其实使用next数组判断字符串是否存在的时候,也是同样的道理,因为同样是有记忆的对比,原理一致。

void getNext(int* next,const string &s){
        int i,j=0;
        next[0]=0;
        for(i=1;i0&&s[j]!=s[i]) j=next[j-1];
            if(s[j]==s[i]) j++;
            next[i]=j;
        }
    }
    int strStr(string haystack, string needle) {
        if(needle.size()==0) return 0;
        int next[needle.size()];
        getNext(next,needle);
        int i=0,j=0;
        for(;i0&&haystack[i]!=needle[j]){
                j=next[j-1];
            }
            if(haystack[i]==needle[j]){
                j++;
                
            }
            if(j==needle.size()) return i-needle.size()+1;
        }
        return -1;
    }

459.重复的子字符串  

本题算是KMP算法的一个应用,不过 对KMP了解不够熟练的话,理解本题就难很多。 我的建议是 KMP和本题,一刷的时候 ,可以适当放过,了解怎么回事就行,二刷的时候再来硬啃。

题目链接/文章讲解/视频讲解:代码随想录

  1. (next[len - 1] != -1)表明整个字符串,存在相同的前后缀,这里要判断一下是因为%后面不能为0。
  2. len % (len - (next[len - 1] + 1)) == 0,也就是当整个字符串的长度可以被(整体长度-最长后缀长度)整除的时候,证明字符串是重复子串构成。
  3. 如图所示,能证明当字符串是重复子串构成时候,len % (len - (next[len - 1] + 1)) == 0成立。但是其实没有反过来证明,公式成立的时候一定是这种情况,但是肯定是找不到反例的,因为电脑都说正确了。

代码随想录算法训练营第七天|KMP算法-28. 实现 strStr()、459.重复的子字符串、字符串总结、双指针回顾_第1张图片

 bool repeatedSubstringPattern (string s) {
        if (s.size() == 0) {
            return false;
        }
        int next[s.size()];
        getNext(next, s);
        int len = s.size();
        if (next[len - 1] != -1 && len % (len - (next[len - 1] + 1)) == 0) {
            return true;
        }
        return false;
    }
bool repeatedSubstringPattern(string s) {
        if(s.size()==0) return false;
        int next[s.size()];
        getNext(next,s);
        if(next[s.size()-1]!=0&&(s.size()%(s.size()-next[s.size()-1])==0)) return true;
        return false;
    }

字符串总结 

比较简单,大家读一遍就行 

题目链接/文章讲解:代码随想录

双指针回顾 

此时我们已经做过10到双指针的题目了,来一起回顾一下,大家自己也总结一下双指针的心得 

文章讲解:代码随想录

你可能感兴趣的:(代码随想录训练营,算法)