KMP 专题知识

 

     http://www.cnblogs.com/zhangtianq/p/5839909.html 

     这个博客主要是理解KMP(字符串普通匹配)

普通next:

 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 }
basic next

普通kmp:

 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 }
kmp 这里求第一个相同串的位置

 

下面针对next数组的其他应用:

1、求模式串在目标串中的出现次数;

     修改了一下kmp即可:

 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 }
kmp 求相同串的个数(可重叠)
 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 }
kmp 不可重叠

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

 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 }
View Code

///////////////////////////////////////////////// 假装是条分割线 /////////////////////////////////////////////////////

需要补充:

字符串的最小表示/最大表示。

posted @ 2017-09-14 19:48  小可爱的小可爱  阅读(189)  评论(0编辑  收藏  举报