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]=-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