【字串匹配】【BKDRhash||KMP】
阿新 • • 發佈:2019-01-01
題目描述 給定一個字串 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 空間限制:256MB256MBT
這道題題意很簡單,是字串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 longlong 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\n",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 }