http://www.cnblogs.com/zhangtianq/p/5839909.html
这个博客主要是理解KMP(字符串普通匹配)
普通next:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 ///这里还是不要用next命名数组啦 2 3 void findnext() 4 { 5 memset(nextt,0,sizeof(nextt)); 6 int j=0,k=-1; 7 nextt[0]=-1; 8 int len=strlen(b); 9 while(j<len) 10 //while(j<len-1) 11 { 12 if(k==-1||b[k]==b[j]) 13 { 14 k++;j++ 15 nextt[j]=k; 16 } 17 else 18 k=next[k]; 19 } 20 }
普通kmp:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 int kmp() 2 { 3 int i=0,j=0; 4 int sa=strlen(a),sb=strlen(b); 5 while(i<sa&&j<sb) 6 { 7 if(j==-1||a[i]==b[j]) 8 { 9 i++; 10 j++; 11 } 12 else 13 { 14 j=nextt[j]; 15 } 16 } 17 if(j==sb) 18 return i-j+1; 19 else 20 return -1; 21 }
下面针对next数组的其他应用:
1、求模式串在目标串中的出现次数;
修改了一下kmp即可:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 ///可重叠 2 3 int kmp() 4 { 5 int i=0,j=0,ans=0; 6 int sa=n,sb=m; 7 while(i<sa) 8 { 9 if(j==-1||a[i]==b[j]) 10 { 11 i++; 12 j++; 13 } 14 else 15 { 16 j=nextt[j]; 17 } 18 if(j==sb) 19 { 20 j=nextt[j];ans++; 21 } 22 } 23 return ans; 24 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 int kmp() 2 { 3 int i=0,j=0,ans=0; 4 int sa=strlen(a),sb=strlen(b); 5 while(i<sa) 6 { 7 if(j==-1||a[i]==b[j]) 8 { 9 i++;j++; 10 } 11 else 12 j=nextt[j]; 13 if(j==sb) 14 { 15 j=0;ans++; 16 } 17 } 18 return ans; 19 }
2、求一个字符串中最长子串(整一个字符串=n*子串)。
比如字符串:abcabcabc 。目测其最长子串为:abc,长度为3。
next数组中,每一个字符的值:
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | len |
字母 | a | b | c | a | b | c | a | b | c | \0 |
next | -1 | 0 | 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
next[len] :是在字符串最后一个字符后面的假设的一个位置。也就是说要是b[len]匹配失败,但在这之前,已经有6个字符时匹配成功的了,因此len-next[len],就是重复的那个子串的长度。这里要注意,如果字符串是“abcabcab”的话,字符串并不能被这个子串完美表示,因此要用len%(len-next[len]) 验证一下,要是==0,就是完美表示且len-next[len]是最小循环节,否则就没有。
3、求一个字符串所有前缀字符和后缀字符匹配的长度:
(给出一个字符串S,长度为len;找出一个前缀一个后缀,使得这两个字符串相同。 输出所有可能的情况)
前面不是讲了那个len-next[len] 是最小的循环节嘛,那你想哦,s[1] -- s[next[len]] 跟 s[len-next[len]+1] -- s[len] 一定是匹配的。所以一个是头,一个就是尾吖。
这样求出的s[1]——s[next[len]]就是我们要求取的最长的那个串,长度是:next[len] 。然后然后,要求所有的话,我们循环地利用next,由于next的性质,可以保证,每一次得出的字串都能匹配到最后一个字母,也就是得到一个前缀等于后缀。只不过这个字符串的长度在不断地减小罢了。不断地使用next我们直到求出所有的前缀。
4、NEXT 求最长前缀后缀子串长度
题目:https://www.cnblogs.com/767355675hutaishi/p/4425987.html
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 int next[100] ; 2 int nextt[10005]; 3 int n,m; 4 string a,b; 5 6 void getNext( ) { 7 int len = a.size() ; 8 next[0] = 0 ; 9 int i = 0 , j = 0 ; 10 for(j = 1 ; j < len ; j++) { // i 代表最长前缀后缀长度 11 while( a[i] != a[j] && i > 0 ) // 当p[i] != p[j] 时,减小最长前缀后缀长度 12 i = next[i-1] ; 13 if(a[i] == a[j]) { 14 i++ ; //最长公共前缀后缀长度+1 15 next[j] = i ; //匹配失败时跳到该处 16 } 17 else 18 next[j] = 0 ; 19 } 20 } 21 22 int main() { 23 cin>>a; 24 int len =n= a.size(); ; 25 getNext(); 26 len=next[len-1]; 27 while(len>0){ 28 b=a.substr(0,len); 29 m=b.size(); 30 findnext(); 31 int x=kmp(); 32 if(x<=2) 33 len--; 34 else{ 35 cout<<b<<endl;break;} 36 } 37 if(len==0) 38 printf("Just a legend\n"); 39 return 0 ; 40 } 41 42 int main() { 43 cin>>a; 44 int len =n= a.size(); ; 45 getNext(); 46 len=next[len-1]; 47 return 0 ; 48 }
/ 假装是条分割线 /
需要补充:
字符串的最小表示/最大表示。