p数组记录的是当该位置上失配的时候,跳到第几个进行继续匹配;
1 /* 2 题意:给两个数串,求模板串第一此出现在源串中的位置,开头的位置;没有输出-1; 3 算法:kmp 4 先对字符串进行自匹配; 5 然后串间匹配; 6 */ 7 #include<iostream> 8 #include<cstdio> 9 #include<cstring> 10 #include<string> 11 using namespace std; 12 int s[1000005],t[10005],p[10005];//s是源串,t是模板串,p是自匹配数组(next); 13 int n,m; 14 int kmp()//模板; 15 { 16 int ans=0; 17 p[0]=p[1]=0; 18 //p数组是错位排列的,p[i]表示的是p[i-1]的信息;即到目前为止的最长前缀; 19 for(int i=1;i<m;i++) 20 { 21 int j=p[i]; 22 while(j&&t[i]!=t[j]) 23 { 24 j=p[j];//定位j的位置; 25 } 26 p[i+1]=t[i]==t[j]?j+1:0; 27 } 28 int j=0,temp=0; 29 for(int i=0;i<n;i++) 30 { 31 while(j&&s[i]!=t[j]) 32 { 33 j=p[j]; 34 } 35 if(s[i]==t[j]) 36 j++; 37 if(j==m) 38 return i+1-m+1; 39 } 40 return 0; 41 //可以判断是否有子串,也可以计算有多少子串; 42 } 43 int main() 44 { 45 int T; 46 scanf("%d",&T); 47 while(T--) 48 { 49 memset(s,0,sizeof(s)); 50 memset(t,0,sizeof(t)); 51 scanf("%d%d",&n,&m); 52 for(int i=0;i<n;i++) 53 scanf("%d",&s[i]); 54 for(int i=0;i<m;i++) 55 { 56 scanf("%d",&t[i]); 57 } 58 int ans=kmp(); 59 if(ans==0) 60 printf("-1 "); 61 else 62 printf("%d ",ans); 63 } 64 return 0; 65 }
下面是自己按照俞勇的书敲的模板;比较详细一点,理解更深了一点;
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 using namespace std; 6 string s,pattern; 7 int next[10100]={0}; 8 int n,m; 9 int position[100000]={0};//标记每一个字串的起始位置; 10 void kmp() 11 { 12 for(int i=1;i<n;i++) 13 { 14 int j=i; 15 while(j>0) 16 { 17 j=next[j]; 18 if(pattern[j]==pattern[i]) 19 { 20 next[i+1]=j+1; 21 break; 22 } 23 } 24 } 25 for(int i=0,j=0;i<m;i++) 26 { 27 //i标记的是大串上的位置,j标记的是小串上的位置; 28 if(j<n&&s[i]==pattern[j]) 29 j++; 30 else 31 { 32 // 如果失配j就跳到小串的下一个位置继续匹配; 33 while(j>0)//j等于0就是没有继续可跳的位置; 34 { 35 j=next[j]; 36 if(s[i]==pattern[j]) 37 { 38 j++; 39 break; 40 } 41 } 42 } 43 if(j==n) 44 cout<<i-n+1+1<<endl;//返回起始位置(i-n+1)是数组中的位置,再加1是从一开始的位置定位; 45 } 46 //没有子串,在这里可以返回-1; 47 } 48 int main() 49 { 50 cin>>s>>pattern; 51 n=pattern.size(); 52 m=s.size(); 53 kmp(); 54 return 0; 55 }