题目链接:hdu3336
题目大意:求字串中【前缀+跟前缀相同的子串】的个数?
Sample Input
1
4
abab
Sample Output
6
abab:包括2个a,2个ab,1个aba,1个abab
思路: 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:
cns[]数组:以 i 结尾的串中所有前缀的计数和
状态转移方程: cns[i] = cns[next[i]] + 1;
i : 123456
字符串: ababab
cns[i]: 112233
比如当i等于5时,ababa中后面的aba与前面的aba重叠,
以第三个a为结尾的前缀总数对应于前面的aba的前缀数+1(ababa)
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
using namespace std;
#define maxn 200020
#define mod 10007
char s[maxn];
int nexts[maxn];
int n,m;
int dp[maxn];
void getnexts()//因为s字符串的前缀匹配t字符串的后缀 所以把s作为匹配的字符串 求nexts
{
int j=0;
int k=-1;
nexts[0]=-1;
while(j<m)
{
if(k==-1||s[j]==s[k])
{
j++;k++;
nexts[j]=k;
}
else k=nexts[k];
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%s",&m,s);
getnexts();
dp[0]=0;
int ans=0;
for(int i=1;i<=m;i++)
{
dp[i]=dp[nexts[i]]+1;
dp[i]%=mod;
ans+=dp[i];
ans%=mod;
}
printf("%d\n",ans);
}
return 0;
}