题解:SP32895 STRMATCH - Match me if you can

简要题意:给定一个文本串 \(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;
}
posted @ 2025-11-25 16:22  Jokersen  阅读(1)  评论(0)    收藏  举报