题解: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;
}
posted @ 2024-05-30 17:36  一只小咕咕  阅读(84)  评论(0)    收藏  举报