http://acm.hdu.edu.cn/showproblem.php?pid=3336
根据TMP求next数组的原理我们发现:
给出任意数字i,它的next值为j(j>0),那么j-1代表str[0]~str[j-1]这个前缀再主串中出现一次。
由此轻松解决本题
AC代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
char str[200010];
int next[200010],n;
void getnext(){
int i=0, j = -1;
next[0] = -1;
while(i < n){
if(j == -1 || str[i] == str[j]){
i++, j++;
next[i] = j;
}
else j = next[j];
}
}
int main(){
int cas;
scanf("%d",&cas);
for(int i=0; i<cas; i++){
scanf("%d",&n);
scanf("%s",str);
getnext();
int ans = 0;
for(int i=1; i<=n; i++)
if(next[i]) ans++;
ans %= 10007;
ans += n%10007;
ans %= 10007;
printf("%d\n",ans);
}
//system("pause");
return 0;
}
上述解法经指正是错误的,正确解法如下:
定义dp[i]表示已i结尾的前缀所包含的前缀数,那么dp[i] =dp[next[i]]+1。这个转移方程什么意思呢?结合abab讲解一下
next[i] = k,那么str[i-k+1~i]就和str[1~k]是完全匹配的,于是转移方程就显而易见。
AC代码:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
char T[200010];
int dp[200010],next[200010],n;
void getnext(){
int i = 0, j = -1;
next[0] = -1;
while(i < n){
if(j == -1 || T[i] == T[j]){
i++, j++;
next[i] = j;
}
else j = next[j];
}
return;
}
int main(){
int cas; cin >> cas;
while(cas--){
cin >> n;
cin >> T;
getnext();
memset(dp, 0, sizeof(dp));
int sum = 0;
for(int i=1; i<=n; i++){
dp[i] = dp[next[i]] + 1;
sum = (sum + dp[i]) % 10007;
}
cout << sum << endl;
}
//system("pause");
return 0;
}