题解: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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步