演算法 - 字串演算法 - 字串雜湊與經典例題《好文章》
阿新 • • 發佈:2022-04-17
nodgd 寫了一篇文章,自認為這是一篇好文章。nodgd 的文章由 \(n\) 個小寫英文字母組成。文章的一個子串指的是文章中的一段連續的字母,子串的長度就是這一段的字母個數。nodgd 在文章中用了排比、對偶、前後照應之類的手法,所以就有很多個子串是相同或者相近的。為了向大家證明這是一篇好文章,nodgd 決定給自己的文章進行評分。nodgd 首先確定了一個整數 \(m\),然後統計出文章中有多少個不相同的長度為 \(m\) 的子串,這個數量就是文章的評分。然而,nodgd 懶得老老實實計算這個評分了,就把任務丟給了你。
輸入格式
第一行包含兩個整數 \(n,m\),表示文章的長度和需要統計的子串長度。
第二行包含一個長度為 \(n\) 的只包含小寫字母的字串。
輸出格式
輸出一行一個整數,表示文章的評分。
輸入輸出樣例
樣例 1
輸入
5 3
aaaab
輸出
2
長度為 \(3\) 的子串有 \(3\) 個,分別是 aaa、aaa、aab,其中不同的只有 \(2\) 個。
樣例 2
輸入
9 3
abcabacba
輸出
7
共有7個長度為3的子串,每個長度為3的子串都不同。
資料範圍
於 \(30\%\) 的資料,\(1 ≤ m ≤ n ≤ 200\);
於 \(50\%\) 的資料,\(1 ≤ m ≤ n ≤ 2000\);
於 \(70\%\) 的資料,\(1 ≤ m ≤ 50 ≤ n ≤ 200000\)
於 \(100\%\) 的資料,\(1 ≤ m ≤ n ≤ 200000\);
題解
使用雙雜湊,這裡我們開兩個hash表,然後給同一個字串附上兩個hash值,在判斷兩個字串是否相同時,必須兩個hash值都相同才行。
#include<cmath> #include<cstdio> const int maxm=200010; long long hash_a[maxm],hash_b[maxm]; long long Hash_a[maxm],Hash_b[maxm]; long long Hash_pow_a[maxm],Hash_pow_b[maxm];//指數陣列 int n,m,ans; char str[maxm]; void Init(int p1,int P1,int p2,int P2) { for(int i=1;i<=n;i++) { Hash_a[i]=(1ll*Hash_a[i-1]*p1+(str[i]-'a'+1))%P1; Hash_pow_a[i]=1ll*Hash_pow_a[i-1]*p1%P1; Hash_b[i]=(1ll*Hash_b[i-1]*p2+(str[i]-'a'+1))%P2; Hash_pow_b[i]=1ll*Hash_pow_b[i-1]*p2%P2; } } int hash_(int l,int r,int p,int P,int k) { if(k==1) return (Hash_a[r]-1ll*Hash_a[l-1]*Hash_pow_a[r-l+1]%P+P)%P; return (Hash_b[r]-1ll*Hash_b[l-1]*Hash_pow_b[r-l+1]%P+P)%P; } void _qst(int l,int r)//快排,可以使用sort,開結構體 //根據HashA的值排序。 { int i,j,m,mm,t; i=l;j=r; m=hash_a[(i+j)>>1];//位運算,相當於除以2 mm=hash_b[(i+j)>>1]; while(i<=j) { while(hash_a[i]<m||(hash_a[i]==m&&hash_b[i]<mm))i++; while(hash_a[j]>m||(hash_a[j]==m&&hash_b[j]>mm))j--; if(i<=j) { t=hash_a[i];hash_a[i]=hash_a[j];hash_a[j]=t; t=hash_b[i];hash_b[i]=hash_b[j];hash_b[j]=t; i++;j--; } } if(i<r)_qst(i,r); if(l<j)_qst(l,j); } int main() { scanf("%d%d",&n,&m); scanf("%s",str+1); Hash_pow_a[0]=1; Hash_pow_b[0]=1; Init(61,1000000007,97,1000000009); for(int i=1;i<=n-m+1;i++) { hash_a[i]=hash_(i,i+m-1,61,1000000007,1); hash_b[i]=hash_(i,i+m-1,97,1000000009,2); } _qst(1,n-m+1); for(int i=1;i<=n-m+1;i++) { if(hash_a[i]==hash_a[i-1]&&hash_b[i]==hash_b[i-1]) continue; ans++; } printf("%d",ans); }