kmp专题

 1 int nex[maxn];
 2 void get_nex(char str2[])
 3 {
 4     nex[0]=0;
 5     int len=strlen(str2);
 6     int i=0;
 7     for(int i=1; i<len; i++)
 8     {
 9         int j=nex[i-1];
10         while(j&&str2[i]!=str2[j])
11             j=nex[j-1];
12         if(str2[i]==str2[j])
13             nex[i]=j+1;
14         else
15             nex[i]=0;
16     }
17 }
18 int kmp(char str1[],char str2[])
19 {
20     get_nex(str2);
21     int len1=strlen(str1);
22     int len2=strlen(str2);
23     int i=0,j=0,ans=0;
24     while(i<len1&&j<len2)
25     {
26         if(str1[i]==str2[j])
27         {
28             i++,j++;
29         }
30         else
31         {
32             if(!j)
33                 i++;
34             else
35                 j=nex[j-1];
36         }
37         if(j==len2)
38         {
39             j=nex[j-1];
40             ans++;
41         }
42     }
43     return ans;
44 }
nex[0]=0 开头的kmp模板

 

nex[0]=-1开头的kmp模板

 

题目链接:https://cn.vjudge.net/contest/276379#problem/A

A题:(kmp模板题)

我的理解:首先nex数组的作用就是判断当前位置是否具有和使得前缀和与后缀和相等,如果存在的话,那么可以直接跳过前缀和,从前缀和的下一个开始匹配。

AC代码:

 1 #include<iostream>
 2 #include<stdio.h>
 3 using namespace std;
 4 # define ll long long
 5 const int maxn = 1000000+100;
 6 int a[maxn],b[maxn];
 7 int nex[maxn];
 8 int n,m;
 9 void getnex()
10 {
11     int i=0,j=-1;
12     nex[0]=-1;
13     while(i<m-1)
14     {
15         if(j==-1||b[i]==b[j])
16         {
17             i++;
18             j++;
19             nex[i]=j;
20         }
21         else
22         {
23             j=nex[j];
24         }
25     }
26 }
27 int kmp()
28 {
29     int i=0,j=0;
30     while(i<n&&j<m)
31     {
32         if(j==-1||a[i]==b[j])
33         {
34             i++;
35             j++;
36         }
37         else
38         {
39             j=nex[j];
40         }
41     }
42     if(j==m)
43         return i-j;
44     return -1;
45 }
46 int main()
47 {
48     int T;
49     scanf("%d",&T);
50     while(T--)
51     {
52         scanf("%d %d",&n,&m);
53         for(int i=0; i<n; i++)
54         {
55             scanf("%d",&a[i]);
56         }
57         for(int i=0; i<m; i++)
58         {
59             scanf("%d",&b[i]);
60         }
61         getnex();
62         int ans=kmp();
63         if(ans!=-1)
64             ans++;
65         printf("%d\n",ans);
66     }
67     return 0;
68 }

 

B题,s2中查找子串s1的个数。

getnex是对s1进行操作的

AC代码:

 1 #include<iostream>
 2 #include<stack>
 3 #include<cstring>
 4 #include<queue>
 5 #include<stdio.h>
 6 #include<map>
 7 #include<vector>
 8 #include<cmath>
 9 #include<string>
10 using namespace std;
11 # define ll long long
12 const int maxn = 1000000+100;
13 char str1[maxn],str2[maxn];
14 int nex[maxn];
15 void getnex(int t)
16 {
17     nex[0]=-1;
18     int i=0,j=-1;
19     while(i<t-1)
20     {
21         if(j==-1||str1[i]==str1[j])
22         {
23             i++;
24             j++;
25             nex[i]=j;
26         }
27         else
28         {
29             j=nex[j];
30         }
31     }
32 }
33 int kmp(int t1,int t2)
34 {
35     int i=0,j=0;
36     int ans=0;
37     while(j<t2)
38     {
39         if(i==-1||str1[i]==str2[j])
40         {
41             i++;
42             j++;
43         }
44         else
45         {
46             i=nex[i];
47         }
48         if(i==t1)
49             ans++,i=nex[i];//如果已经匹配完毕,回到头就可以了。
50     }
51     return ans;
52 }
53 int main()
54 {
55     int T;
56     scanf("%d",&T);
57     while(T--)
58     {
59         scanf("%s",str1);
60         scanf("%s",str2);
61         int len1=strlen(str1);
62         int len2=strlen(str2);
63         getnex(len2);
64         int ans=kmp(len1,len2);
65         printf("%d\n",ans);
66     }
67     return 0;
68 }
69 /*
70 3
71 AZA
72 AZAZAZA
73 */

 

C题:

寻找最少加几个字母,能够使得新得到的字符串是由循环节构成的。

具体思路:首先找到满足初始子串的循环节,然后再判断一下,原来字符串长度。

如果能被循环节整除,如果循环节只存在一个,那么直接输出原来的长度就可以了,如果存在多个的话,输出0就可以了。

另一种情况,看还剩下多少余下的,补足就可以了。

AC代码:

 1 #include<iostream>
 2 #include<stack>
 3 #include<stdio.h>
 4 #include<queue>
 5 #include<string>
 6 #include<cstring>
 7 #include<cmath>
 8 using namespace std;
 9 # define ll long long
10 const int maxn = 100000+100;
11 char str[maxn];
12 int nex[maxn];
13 void getnex(int len)
14 {
15     nex[0]=-1;
16     int i=-1,j=0;
17     while(j<len)
18     {
19         if(i==-1||str[i]==str[j])
20         {
21             i++;
22             j++;
23             nex[j]=i;
24         }
25         else
26         {
27             i=nex[i];
28         }
29     }
30 }
31 int main()
32 {
33     int T;
34     scanf("%d",&T);
35     while(T--)
36     {
37         scanf("%s",str);
38         int len=strlen(str);
39         getnex(len);
40         int tmp=len-nex[len];
41         if(len%tmp==0)
42         {
43             if(len/tmp>=2)
44                 printf("0\n");
45             else
46             {
47                 printf("%d\n",len);
48             }
49         }
50         else
51         {
52           //  if(len/tmp>=1)
53                 printf("%d\n",tmp-len%tmp);
54 //            else
55 //            {
56 //                printf("%d\n",len);
57 //            }
58 
59         }
60     }
61     return 0;
62 }

 

D题:

对于每一位,寻找当前这一位之前的循环节个数,如果不为零,则输出。

具体思路:首先是循环节的寻找过程,对于当前这一位的循环节是len-nex[len],这里的len指的并不是总的长度,而是指的是从头开始,到这一位的长度。

AC代码:

 1 #include<iostream>
 2 #include<stack>
 3 #include<iomanip>
 4 #include<stdio.h>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<string>
 8 #include<cstring>
 9 #include<vector>
10 using namespace std;
11 # define ll long long
12 const int maxn = 1e6+10;
13 char str[maxn];
14 int nex[maxn];
15 void getnex(int len)
16 {
17     nex[0]=-1;
18     int i=0,j=-1;
19     while(i<len)
20     {
21         if(j==-1||str[i]==str[j])
22         {
23             i++;
24             j++;
25             nex[i]=j;
26         }
27         else
28         {
29             j=nex[j];
30         }
31     }
32 }
33 int main()
34 {
35     int len;
36     int Case=0;
37     while(~scanf("%d",&len)&&len)
38     {
39         scanf("%s",str);
40         getnex(len);
41 //        for(int i=0;i<=len;i++){
42 //        cout<<i<<" "<<nex[i]<<endl;
43 //        }
44         printf("Test case #%d\n",++Case);
45         for(int i=2; i<=len; i++)
46         {
47             int tmp=i-nex[i];
48             if(tmp==0||nex[i]==0)//如果tmp为0或者nex【i】为0,代表当前这一位之前没有循环节
49                 continue;
50             if(i%tmp==0)//必须能够整除才能算上。
51             {
52                 printf("%d %d\n",i,i/tmp);
53             }
54         }
55         printf("\n");
56     }
57     return 0;
58 }
59  

 

posted @ 2018-12-21 09:29  Let_Life_Stop  阅读(169)  评论(0编辑  收藏  举报