题意很简单:不同长度[1...len]前缀最大重复次数和累加。mod 10007
一个自然的写法,枚举par串(结果是TLE,看来数据比较多)
#include <bits/stdc++.h> using namespace std; const int MAXN=200010; char ori[MAXN],par[MAXN]; int nex[MAXN]; void preKMP(char s[]) { int p=0,q=-1; nex[0]=-1; while(s[p]){ while(q!=-1 && s[p]!=s[q]) q=nex[q]; nex[++p]=++q; } } int KMP(char a[],char b[]) { int p=0,q=0,l=strlen(a),ans=0; preKMP(a); while(b[p]){ while(q!=-1 && a[q]!=b[p]) q=nex[q]; ++p; ++q; if(q==l) { ans++; q=nex[q]; } } return ans; } int main() { int t,len; for(scanf("%d",&t);t;t--) { scanf("%d",&len); scanf("%s",ori); int res=0; for(int i=0;i<len;i++) { par[i]=ori[i]; par[i+1]='\0'; res+=KMP(par,ori); res%=10007; } printf("%d\n",res); } return 0; }
就如同推出next数组一样,从1开始,设dp[i]表示以str[i]结尾的字符串含有的前缀数量,
dp[i] = dp[next[i]] + 1,即长度小于i的前缀数量+长度为i的前缀(1)。
为什么是+1不是加别的,因为如果还存在一个重复子串,在next[i]位置到i位置之间,那么next[i]还会更大(矛盾)。
next数组的性质确保它是首尾端子串的最大匹配,所以后面不存在重复子串,除了长度为i的串本事。
回顾一下KMP和DP:
kmp思想:对字符串进行预处理,记录与当前位置i后缀相同的“最近”位置,用next[i]记录, 保证 s[1 .. i] 中 s[i - next[i] + 1 .. i] 与 s[1 .. next[i]] 是相同的, 以便在某处字符不匹配时,不用重新从头判断一遍,只要从对应的next[i]即可, 因为中间有部分与自身重叠,减少了不必要的判断,实现见代码。 例1: i : 1234567 字符串 : abcabab next[i] : 0001212 例2: i : 12345678 字符串 : abababab next[i] : 00123456
例3: i : 123456789 字符串: ababcdabd next[i]: 001200120 DP: dp[]数组:以 i 结尾的串中所有前缀的计数和 状态转移方程: dp[i] = dp[next[i]] + 1; i : 123456 字符串: ababab dp[i]: 112233
比如当i等于5时,ababa中后面的aba与前面的aba重叠,
以第三个a为结尾的前缀总数对应于前面的aba的前缀数+1(ababa)
#include <bits/stdc++.h> using namespace std; const int MAXN=200010; char ori[MAXN]; int nex[MAXN],dp[MAXN]; void preKMP(char s[]) { int p=0,q=-1; nex[0]=-1; while(s[p]){ while(q!=-1 && s[p]!=s[q]) q=nex[q]; nex[++p]=++q; } } int main() { int t,len; for(scanf("%d",&t);t;t--) { scanf("%d",&len); scanf("%s",ori); int ans=0; preKMP(ori); memset(dp,0,sizeof(dp)); for(int i=1;i<=len;i++) { dp[i]=dp[nex[i]]+1; ans+=dp[i]007; } printf("%d\n",ans007); } return 0; }