题解: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;
}

本文作者:一只小咕咕

本文链接:https://www.cnblogs.com/yzxgg/p/18222922/solution-P10479

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   一只小咕咕  阅读(41)  评论(0编辑  收藏  举报
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.
点击右上角即可分享
微信分享提示