简要题意:给定一个文本串 \(S\),以及若干个模式串 \(T\),按顺序输出每个模式串 \(T\) 在文本串 \(S\) 中出现的次数。
NOIP 模拟赛出了加强版,过来发个题解。
提供一种在线 \(\mathcal O(|S|\sqrt{\sum |T|})\) 的做法:
首先有个结论,就是模式串 \(T\) 的长度的种类数是 \(\mathcal O(\sqrt{\sum |T|})\) 的。
为什么呢?考虑让种类数最多的情况,即长度分别为 \(1,2,3,\dots,k\),这样长度和是 \(\mathcal O(k^2)\) 的,因此结论成立。
考虑字符串哈希,不难做到 \(\mathcal O(1)\) 求出文本串中一个字串的哈希值,于是可以在 \(\mathcal O(n)\) 求出文本串的所有长度为 \(k\) 的字串的哈希值。
然后将求出来的所有长度为 \(k\) 的字串的哈希值丢到一个 unordered_map 里,查询时答案就是查找对应长度的 unordered_map 里模式串的哈希值出现的次数。
下面是一个双模哈希的代码实现:
#include<bits/stdc++.h>
#define N 500005
#define um unordered_map
using namespace std;
const int base1=20120602;
const int base2=20120412;
const int mod1=998244353;
const int mod2=1000000007;
set<int>tmp;string s[N];
um<int,um<int,int>>cnt[N];
int n,q,p1[N],p2[N],h1[N],h2[N];
int main(){scanf("%d%d",&n,&q);p1[0]=p2[0]=1,cin>>s[0];for(int i=1;i<=n;i++){p1[i]=1ll*p1[i-1]*base1%mod1, p2[i]=1ll*p2[i-1]*base2%mod2,h1[i]=(1ll*h1[i-1]*base1+s[0][i-1])%mod1, h2[i]=(1ll*h2[i-1]*base2+s[0][i-1])%mod2;}for(int i=1;i<=q;i++)cin>>s[i],tmp.insert(s[i].size());for(int i=1;i<=n;i++) for(auto p:tmp) if(i>=p){int t1=(h1[i]-1ll*h1[i-p]*p1[p])%mod1;int t2=(h2[i]-1ll*h2[i-p]*p2[p])%mod2;cnt[p][t1+(t1<0?mod1:0)][t2+(t2<0?mod2:0)]++;}for(int i=1;i<=q;i++){int t1=0,t2=0;for(auto p:s[i])t1=(1ll*t1*base1+p)%mod1,t2=(1ll*t2*base2+p)%mod2;printf("%d\n",cnt[s[i].size()][t1][t2]);}return 0;
}