题解:P10479 匹配统计
思路
考虑 kmp,通过观察匹配文本串匹配过程,可以发现模式串所对应的 \(j\) 的含义正是该模式串与文本串匹配到了一个从 \(i-j+1\) 开始的长度为 \(j\) 的公共前缀。
但这并不意味着是恰好匹配 \(j\) 位,因为后面也许还可能可以匹配,但至少可以确定至少可以匹配 \(j\) 位。所以我们直接用一个 \(cnt\) 数组记录一下至少匹配 \(x\) 位的有多少。
最后的结果就是 \(cnt_{x+1} - cnt_x\) (也就是至少匹配 \(x+1\) 位的个数减去至少匹配到 \(x\) 位的个数就是恰好匹配到 \(x\) 的个数)。
同时我们考虑 \(nex\) 数组的性质,发现如果一个以 \(i\) 结尾且与模式串匹配长度为 \(j\) 的文本串,除了从 \(i-j+1\) 开始可以匹配 \(j\) 个,从 \(i-nex_j+1\) 开始也同样可以(因为由于 \(nex\) 记录的是最长公共前后缀的缘故,所以文本串的 \(i-j+1\) 和 \(i-nex_j+1\) 往后 \(j\) 个是一样的),同理 \(j-nex_{nex_j}+1\) 也是可以的,可以往后以此类推。
朴素的,我们可以想到让 \(cnt_j\) 加一的同时,让 \(cnt_{nex_j}\) 以及后面的都加一,但这样显然会超时。观察下我们操作 \(cnt_j\) 的过程,每次都会让 \(cnt_{nex_j}\) 加一,也就是说,\(cnt_j\) 操作了多少次,\(cnt_{nex_j}\) 也就要相应的执行多少次。那么我们就可以先只让 \(cnt_j\) 加一,最后从 \(m\) 到 \(1\) 枚举一遍,让 \(cnt_{nex_i}\) 加上 \(cnt_i\) 就行了。
时间复杂度 \(\mathcal{O}(n+m+q)\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
inline int read();
int n,m,q,nex[N],cnt[N];
char a[N],b[N];
int main()
{
n=read(); m=read(); q=read();
scanf("%s",(a+1));
scanf("%s",(b+1));
int j=0;
for(int i=2; i<=m; i++)
{
while(j && b[i] != b[j+1]) j=nex[j];
if(b[i] == b[j+1]) nex[i]=j+1;
j=nex[i];
}
j=0;
for(int i=1; i<=n; i++)
{
while(j && a[i] != b[j+1])
j=nex[j];
if(a[i] == b[j+1]) j++;
cnt[j]++;
}
for(int i=m; i>=1; i--) cnt[nex[i]]+=cnt[i];
while(q--)
{
int x;
x=read();
printf("%d\n",cnt[x]-cnt[x+1]);
}
return 0;
}
inline int read()
{
int x=0,f=1;
char ch;
ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-') f=-f; ch=getchar();}
while(ch<='9' && ch>='0')
{
x=(x<<1)+(x<<3)+(ch&15);
ch=getchar();
}
return x*f;
}

浙公网安备 33010602011771号