题意:例如abab这样的字符串的子字符串看为有a,ab,aba,abab四种,就是从第一个开始,每次加一个元素,也就是所有的非空前缀,其中a,ab在原字符串中出现两次,aba,abab各出现一次,所以一共六次,结果要%10007
对,每次都是从0开始更新,很慢
分析一下这个ac代码,每次都重新定义一个新的子字符串太浪费时间了,那就要学会利用next数组来减少麻烦,其实这道题从一开始就想的复杂了,为什么每次都要找一个新的字符串呢?没必要的
next数组存放的是字符串的前缀和后缀能匹配的字符个数的最大值
如果next[i] == 0或-1则表示 由s的前i个字符组成的字符串的所有后缀肯定和其前缀不匹配。
否则 由s的前i个字符组成的字符串存在某个前缀和后缀匹配的情况,也就是该前缀的出现的次数应该加上1。
例如 0 1 2 3 4 5 6 7 8
a b a b a b a b
-1 0 0 1 2 3 4 5 6
这样一个例子,先判断ne[len] = 6 != 0或-1,接着判断ne[ne[len]]即ne[6],仍然符合不等于1或是0,直到判断到结束条件时,如果ne[]某个数值等于-1或0时,结束判断
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
#define maxn 200005
int len;
char a[maxn];
int ne[maxn];
void findnext()
{
int i = 0, j = -1;
ne[0] = -1;
while(i < len)
{
if(j == -1 || a[i] == a[j])
ne[++i] = ++j;
else j = ne[j];
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&len);
scanf("%s",a);
long long ans = len;//每个前缀至少会出现一次,所以初始化答案设为所有前缀的个数即字符串长度
findnext();
for(int i = len; i > 0; i --)//倒叙遍历每一个点
{
while(ne[i] != 0 && ne[i] != -1)
{
ans ++;//符合条件时就加上该种情况
ne[i] = ne[ne[i]];//跳到下一个位置
}
if(ans >= 10007) ans %= 10007;
}
printf("%lld\n",ans);
}
return 0;
}