HDU-3336 Count the string (KMP)
题意:T个问题,m为字符串长度,然后输出匹配所有前缀串出现个数之和,其中 mod为 10007
思路:一开始可以想到利用kmp匹配统计子字符串出现次数的模板 (用一个for循环来一个一个匹配)但是字符串长度为(1 <= n <= 200000) 所以for下去肯定会超时,所以我们尝试用dp解决(记录一些共同的信息) 我们设置dp[i]表示该字符串前i个字符中出现任意以第i个字符结尾的前缀的次数,next[i]是最长公共前后缀也是回溯距离。事实上这个也是 KMP对循环节处理部分的一个运用(回溯找到相同前后缀);
所以推出dp[i]=dp[next[i]]+1这样一个等式(就能减少重复匹配了);(上一个回溯字符尾部相同)
完整代码:
#include <iostream> #include <cstring> #include <cmath> #define mod 10007 using namespace std; const int maxn = 2e5+9; int dp[maxn],nex[maxn]; char s[maxn]; int len1,len2; void getnext(){ int i=0,j=-1; nex[i] = j; while(i<len1){ if(j==-1||s[i]==s[j]){ nex[++i] = ++j; }else{ j = nex[j]; } } } int main(){ int T; scanf("%d",&T); while(T--){ int sum = 0; scanf("%d",&len1); scanf("%s",&s); memset(dp,0,sizeof(dp)); getnext(); for(int i=1;i<=len1;i++){ dp[i]=dp[nex[i]]+1; sum = (sum+dp[i])%mod; } printf("%d\n",sum); } }
一开始写for循环的TLE代码:(记住字符串的TLE 问题以后不能再犯...)
#include <iostream> #include <cstring> #include <cmath> #define mod 10007 using namespace std; const int maxn = 2e5+9; int nex[maxn]; char s[maxn]; char s1[maxn]; int len1,len2; void getnext(){ int i=0,j=-1; nex[i] = j; while(i<len1){ if(j==-1||s[i]==s[j]){ nex[++i] = ++j; }else{ j = nex[j]; } } } int kmp_count(){ int ans = 0; int i= 0,j=0; for(i=0;i<len1;i++) { while(j>0&&s[i]!=s1[j]) j=nex[j]; if(s[i]==s1[j]) j++; if(j==len2) { ans++; j=nex[j]; } } return ans; } int main(){ int T; cin>>T; while(T--){ int ans = 0; scanf("%d",&len1); scanf("%s",&s); getnext(); for(int i=0;i<len1;i++){ len2 = i+1; s1[i]=s[i]; ans = (ans + kmp_count())% mod ; } cout<<ans<<endl; } }