A Secret HDU - 6153 扩展KMP || KMP
题目链接:https://vjudge.net/problem/HDU-6153
题意
求一个串T的所有后缀在串S中出现的次数.
给定母串str1,匹配串str2
str2[i-n]在str1中出现的次数为num[i]
str2[i-n]的长度为len[i]
求 (0<=i<=n,n为strlen(str2)-1) 。
扩展KMP解法
可以利用拓展KMP求出S的每一个后缀和T的最长公共前缀。
假如当前最长公共前缀为k,就说明长度为k的前缀在S中出现了一次,并且这个k前缀不能构成k+1前缀。用一个cnt数组将各种长度前缀出现的次数记录下来。
对于样例2
abababab
aba
首先将样例翻转,得到
babababa
aba
拓展KMP求出的extend数组值如下
extend[0] = 0
extend[1] = 3
extend[2] = 0
extend[3] = 3
extend[4] = 0
extend[5] = 3
extend[6] = 0
extend[7] = 1
所以cnt数组值为
cnt[1] = 1
cnt[2] = 0
cnt[3] = 3
根据cnt数组来求T的前缀在S中出现的次数:
-
长度为3的前缀:出现了3次不解释。
-
长度为2的前缀:在长度为3的前缀中出现过3次,不能构成3前缀的2前缀数目(即cnt[2])等于0,所以2前缀出现了3次。
-
长度为1的前缀:在长度为2的前缀中出现了3次,不能构成2前缀的1前缀数目(即cnt[1])等于1,所以1前缀出现了4次。
问题解决
对于每一个点extend等差数列求和 累加就是答案:
因为每个点extend相当于
代码
#include <bits/stdc++.h> using namespace std; #define int long long const int N=1e6+10; const int mod=1e9+7; string s,t; int slen,tlen; int z[N]; int ext[N]; int cnt[N]; void get_Z(){ int l=0,r=0; z[0]=tlen; for(int i=1;i<tlen;i++){ if(i>=r){ while(i+z[i]<tlen && t[i+z[i]]==t[z[i]]) z[i]++; l=i,r=i+z[i]; } else if(z[i-l]<r-i) z[i]=z[i-l]; else{ z[i]=r-i; while(i+z[i]<tlen && t[i+z[i]]==t[z[i]]) z[i]++; l=i,r=i+z[i]; } } } void get_exKMP(){ int l=0,r=0; while (r < slen && r < tlen && s[r] == t[r]) r++; ext[0] = r; // r=0; for(int i=1;i<slen;i++){ if(i>r){ while(i+ext[i]<slen && ext[i]<tlen && s[i+ext[i]]==t[ext[i]]) ext[i]++; l=i,r=i+ext[i]; }else if(z[i-l]<r-i) ext[i]=z[i-l]; else { ext[i]=r-i; while(i+ext[i]<slen && ext[i]<tlen && s[i+ext[i]]==t[ext[i]]) ext[i]++; l=i,r=i+ext[i]; } } } void solve(){ memset(z,0,sizeof z); memset(ext,0,sizeof ext); cin>>s>>t; slen=s.length(),tlen=t.length(); reverse(t.begin(),t.end()); reverse(s.begin(),s.end()); get_Z(); get_exKMP(); int ans=0; for(int i=0;i<slen;i++){ ans=(ans+(ext[i]*(ext[i]+1)/2)%mod)%mod; } cout<<ans<<endl; } signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int t=1; cin>>t; while(t--) solve(); return 0; }
KMP解法
KMP:和拓展KMP相似。
主要思路就是求出不能构成k + 1前缀的k前缀数目。
和 用KMP统计T串在S串中出现次数 的代码相似,KMP匹配的时候,我们只需要在失配和匹配完成的时候记录一下即可。
设失配的时候已经匹配的长度为k,那么这个k前缀不能构成k + 1前缀。
代码
#include <bits/stdc++.h> using namespace std; #define int long long const int N=1e6+10; const int mod=1e9+7; string s,t; int slen,tlen; int ne[N]; int cnt[N]; void get_next(){ ne[0]=-1; int pre=-1,j=0; while(j<tlen){ //不能使用优化 if(pre==-1 || t[pre]==t[j]){ ne[++j]=++pre; } else pre=ne[pre]; } } void kmp(){ get_next(); int i=0,j=0; // 注意不能是<,否则最后一个模式会遗漏 //可以模拟第二个样例得出 while(i<=slen){ if(j==-1 || s[i]==t[j]) i++,j++; else { cnt[j]++; j=ne[j]; } if(j==tlen) { cnt[j]++; j=ne[j]; } } } void solve(){ memset(cnt,0,sizeof cnt); cin>>s>>t; slen=s.length(),tlen=t.length(); reverse(t.begin(),t.end()); reverse(s.begin(),s.end()); kmp(); int ans=0; for(int i=tlen;i>0;i--){ cnt[i]=(cnt[i]+cnt[i+1])%mod; ans=(ans+cnt[i]*i)%mod; } cout<<ans<<endl; } signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int t;cin>>t; while(t--) solve(); return 0; }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/16659436.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步