1. 程式人生 > >【字串匹配】【BKDRhash||KMP】

【字串匹配】【BKDRhash||KMP】

題目描述
給定一個字串 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
T

這道題題意很簡單,是字串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\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 }