题解:P10479 匹配统计

· · 题解

来个简单的 Hash 做法吧。

打死不用 KMP。

Part 1,about Hash

先给出初始化的代码。

cin>>n>>m>>q;
cin>>a>>b;
a=" "+a,b=" "+b;
power[0]=1;
//前缀 Hash 值进制数选的是 131,Hash 可以把一个前缀字符串压成整数
//预处理前缀 Hash 值,用 ull 自然溢出取模,其实 Hash 的冲突概率很小啦
//power计算是计算进制位权的,用于配合前缀 Hash 值计算每一个字串的 Hash 值
for(int i=1;i<=n;i++) ha[i]=ha[i-1]*131+a[i],power[i]=power[i-1]*131;
for(int i=1;i<=m;i++) hb[i]=hb[i-1]*131+b[i],power[i]=power[i-1]*131; 

接下来需要计算前缀 Hash 值。

可以表示为 $H(s_r)-H(s_{l-1})\times 131^{r-l+1}$。 为什么,要乘以个 $131^{r-l+1}$。 因为要在 $s_{l-1}$ 后补 $r-l+1$ 个空字符才能于 $s_r$ 相减。 因为 $r$ 位字符串于 $l-1$ 位字符串是不一样的。 ```cpp unsigned long long geta(int l,int r){ return ha[r]-ha[l-1]*power[r-l+1]; } unsigned long long getb(int l,int r){ return hb[r]-hb[l-1]*power[r-l+1]; } ``` 接着,我们就可以 $O(1)$ 比较两个子串了。 ## Part 2,Multiply Search 倍增查找,在这里比二分好用。 我们先枚举每个后缀字串的开头 $b$。 去枚举匹配结尾的下一个位置 $e$。 如 $a[b,e)$ 的 Hash 值等于 $b[1,e-b+1)$ 那么就匹配上了。 匹配值取值 $[0,1]$。 又发现,匹配值满足单调性。 直接上倍增。 这里上倍增的原因是可以设置两个指针 $p,q$。 一个在 $a$ 上面指,一个在 $b$ 上面指。 最后建立一个桶,去统计 $q-1$ 就行了。 代码。 ```cpp #include<bits/stdc++.h> using namespace std; int n,m,q; string a,b; unsigned long long ha[200010],hb[200010],power[200010]; unsigned long long geta(int l,int r){ return ha[r]-ha[l-1]*power[r-l+1]; } unsigned long long getb(int l,int r){ return hb[r]-hb[l-1]*power[r-l+1]; } int buc[2000100]; int main() { cin>>n>>m>>q; cin>>a>>b; a=" "+a,b=" "+b; power[0]=1; for(int i=1;i<=n;i++) ha[i]=ha[i-1]*131+a[i],power[i]=power[i-1]*131; for(int i=1;i<=m;i++) hb[i]=hb[i-1]*131+b[i],power[i]=power[i-1]*131; for(int i=1;i<=n;i++){ int p=i,q=1;//两个指针 for(int i=30;i>=0;i--){ if(p+(1<<i)-1>n || q+(1<<i)-1>m) continue; if(geta(p,p+(1<<i)-1)==getb(q,q+(1<<i)-1)) p+=(1<<i),q+=(1<<i);//Hash 值倍增 } buc[q-1]++;//统计 } while(q--){//求解 int x; cin>>x; cout<<buc[x]<<"\n"; } return 0; } ```