Hash——字符串匹配(求s1在s2中出現的次數)
題目描述:
這是一道模板題。
給定一個字符串 A 和一個字符串 B ,求 B 在 A 中的出現次數。A 和 B中的字符均為英語大寫字母。
求A 在 B 中出現了幾次。(可重疊)
樣例輸入:
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
樣例輸出:
1
3
0
首先要知道什麽是字符串hash(滾動哈希):
單哈希可以O(m)的時間計算長度為m的字符串的哈希值,但對於本題,總的時間復雜度沒有改觀。時間會爆。
這時我們就需要一個叫做滾動哈希的優化技巧。
我們選取兩個合適的互質常數b和h(b<h),假設字符串C=c
正常數字是十進制的,這裏b是基數,相當於把字符串看做是b進制數。
這一過程是遞推計算的,設H(C,k)為前k個字符構成的字符串的哈希值,則:(以下均不考慮取模的情況)
H(C,k+1)=H(C,k)× b + ck+1
字符串哈希,通常題目要求的是判斷主串的一段字符串與另一個匹配串是否匹配,即判斷字符C=c1c2……cm從位置k+1開始的長度為n的子串C‘=ck+1ck+2……ck+n
H(C‘)=H(C,k+n) - H(C,k) × bn
於是我們只要預求得b,就能在O(1)時間內得到任意字符串的字符串的子串哈希值,從而完成字符串匹配,那麽上述字符串匹配問題的算法復雜度就為O(n+m)。
在實現算法時,可以利用32位或64位無符號整數計算hash值(如:unsigned long long),並取h=232或h=264,通過子讓溢出省去取模運算。
——By《一本通》
那麽本題就可以用上述方式AC了(書上代碼有bug,需自己改動)
代碼如下:
#include<cstdio> #include<cstring> using namespace std; #define ULL unsigned long long #define K 103 int N; char s1[1000005], s2[1000005]; ULL f[1000005],l1,l2,t; ULL a[1000005]; ULL get(int x,int y) { return f[y]-f[x-1]*a[y-x+1]; } int main() { //freopen("字符串匹配(求s1在s2中出現的次數).in","r",stdin); //freopen("字符串匹配(求s1在s2中出現的次數).out","w",stdout); scanf("%d",&N); a[0]=1; for(int i=1;i<=1000000;++i)//預處理出a^n a[i]=a[i-1]*K; for(int i=1;i<=N;++i) { int ans(0);t=0; scanf("%s%s",s2+1,s1+1); l1=strlen(s1+1);l2=strlen(s2+1); for(int j=1;j<=l1;++j) f[j]=f[j-1]*K+(s1[j]-‘A‘);//計算主串的滾動哈希值 for(int j=1;j<=l2;++j) t=t*K+(s2[j]-‘A‘);//計算匹配串的哈希值 for (int j=1;j+l2-1<=l1;++j) { if(get(j,j+l2-1)==t)//枚舉起點為i,長度為n的子串,判斷與匹配串是否匹配 ans++; } printf("%d\n",ans);//輸出 } return 0; }
Hash——字符串匹配(求s1在s2中出現的次數)