题意:
给出一个字符串,求这个字符串所有前缀在字符中出现的个数。
题解:
很明显不能用暴力,kmp的扩展问题,分析这种问题最好是先写出next的代码,然后运行观察next数组对应值和字符串之间的关系,找出变化规律。
next[i]的意义表示以i-1结尾的子串中,既是真后缀又是最长前缀的长度。打印了abab这个例子的next数组变化值如下:
——————————————
i | 0 | 1 | 2 | 3 | 4 |
——————————————
s[i] | a | b | a | b |\0 |
——————————————
next[i]| -1| 0 | 0 | 1 | 2 |
——————————————
发现上面表格的规律,例如i=4时,对应0-3对应的最长公共前后缀串长度2,这个值刚好是等于a出现一次,ab出现一次的和,也就是前缀在字符串出现的次数(不包含前缀本身),但是会发现0-4的next中会重复计算,但有一点是肯定的只要 next[i]+1!=next[i+1]这样不满足递推条件,那么只单单计算以i为结尾的next[i]就可以了,那么个计算的出现次数是不包含前缀本身的,所以最后还要加上前缀的数量。
所以综上所述 ans={所有满足next[i]+1!=next[i+1]的next[i]的值}+len
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<map>
using namespace std;
//typedef long long lld;
const int oo=0x3f3f3f3f;
//const lld OO=1LL<<61;
const int MOD=10007;
const int maxn=200005;
char str[maxn];
int next[maxn];
void get_next(int len)
{
int i=0;next[i]=-1;
int j=-1;
while(i<len)
{
if(j==-1||str[i]==str[j])
{
i++;j++;
next[i]=j;
}
else
j=next[j];
}
}
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d%s",&n,str);
get_next(n);
int ans=0;
next[n+1]=0;
for(int i=1;i<=n;i++)
if(next[i]+1!=next[i+1])
ans=(ans+next[i]+MOD)%MOD;
ans=(ans+n+MOD)%MOD;
printf("%d\n",ans);
}
return 0;
}
/**
abcd
-> abcdabc
abcda
*/