题目描述 给定一个字符串 A 和一个字符串 B ,求 B 在 A 中的出现次数。A 和 B中的字符均为英语大写字母或小写字母。 A 中不同位置出现的 B 可重叠。 输入格式 输入共两行,分别是字符串 A 和字符串 B 。 输出格式 输出一个整数,表示 B 在 A 中的出现次数。 样例一 input zyzyzyz zyz output 3 限制与约定 对于100%的数据:1≤A,B的长度≤106,A、B仅包含大小写字母。1≤A,B的长度≤106,A、B仅包含大小写字母。 时间限制:1s1s 空间限制:256MB256MB
这道题题意很简单,是字符串hs和KMP的裸题
字符串hs是一个准确率不是100%的算法,它的做法略玄学
就是把一个字符串按ASC码用一个进制(也叫seed,通常是131,13131)转成一个数字,并且使用unsigned long long自然溢出,
匹配时用小串的一部分和大串的一部分对比hs值实现匹配
代码如下
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define N 1000055 5 #define seed 13131 6 using namespace std; 7 typedef unsigned long long ull; 8 char a[N],b[N]; 9 int alen,blen; 10 long long cnt; 11 ull se[N],aa,abc[N]; 12 inline void init() 13 { 14 scanf("%s%s",a+1,b+1); 15 se[0]=1; 16 alen=strlen(a+1),blen=strlen(b+1); 17 for(int i=1;i<=alen;i++) 18 se[i]=se[i-1]*seed, 19 abc[i]=abc[i-1]*seed+(a[i]-'0'); 20 for(int i=1;i<=blen;i++) 21 aa=aa*seed+(b[i]-'0'); 22 } 23 int main() 24 { 25 init(); 26 for(int i=1;i+blen-1<=alen;i++) 27 { 28 int l=i,r=l+blen-1; 29 if(aa==abc[r]-abc[l-1]*se[r-l+1])cnt++; 30 } 31 printf("%lld ",cnt); 32 return 0; 33 }
然后是一种绝对准确,但是会比hs慢的算法,它叫KMP,名字取自三位大佬的名字的首字母
思想就是在失配时候不是调到头,而是利用之前的信息,跳到一个更佳的位置,从而节约时间复杂度
nxt数组的求法是算法的精髓
1 void getnxt() 2 { 3 int k=0,j=1; 4 nxt[1]=0; 5 while(j<=plen) 6 { 7 if(k==0||p[j]==p[k]) 8 k++,j++,nxt[j]=k; 9 else 10 k=nxt[k]; 11 } 12 }
我习惯右移一位存储所以和网上的代码不太一样
网上很多讲解,不再赘述
那里的nxt[1]=0是最神奇的地方,用手模拟一下会发现当第一位失配时候,这么做就可以巧妙的解决
求的时候这么求
1 int solvebyKMP() 2 { 3 int pt=1,pp=1,fin=0; 4 while(pt<=tlen) 5 { 6 if(t[pt]==p[pp]||pp==0) 7 pt++,pp++; 8 else pp=nxt[pp]; 9 if(pp==plen+1)fin++,pp=nxt[pp]; 10 } 11 return fin; 12 }
然后就能解决,
关于nxt数组的应用还有很多,以后的博客中会讲到
1 #include<cstdio> 2 #include<cstring> 3 #define N 1000111 4 #define clear(a,val) memset(a,val,sizeof(a)) 5 using namespace std; 6 char t[N],p[N]; 7 int tlen,plen; 8 int nxt[N]; 9 void getnxt() 10 { 11 int k=0,j=1; 12 nxt[1]=0; 13 while(j<=plen) 14 { 15 if(k==0||p[j]==p[k]) 16 k++,j++,nxt[j]=k; 17 else 18 k=nxt[k]; 19 } 20 } 21 int solvebyKMP() 22 { 23 int pt=1,pp=1,fin=0; 24 while(pt<=tlen) 25 { 26 if(t[pt]==p[pp]||pp==0) 27 pt++,pp++; 28 else pp=nxt[pp]; 29 if(pp==plen+1)fin++,pp=nxt[pp]; 30 } 31 return fin; 32 } 33 int main() 34 { 35 scanf("%s%s",t+1,p+1); 36 tlen=strlen(t+1),plen=strlen(p+1); 37 getnxt(); 38 printf("%d",solvebyKMP()); 39 return 0; 40 }