洛谷P2375 [NOI2014] 动物园
暴力想法就是求前缀数组,然后倒着一步步得跳j指针直到长度小于当前总长一半,最后把j指针跳到0的次数总和就是答案,当然会T,例如全是'a'
在求前缀数组时可以递推记录当前长度有多少个相同前后缀的个数,此时不管前后缀区间是否重叠
用与求前缀数组同样的想法去跳指针j,每次都得出不超过长度一半的最长前后缀的长度j,不更新j的值就能保证每次j值都在i/2的左边
为何第0个字符要等于1,ac数组记录的相同前后缀个数而不是真前后缀个数
因为在跳j指针的时候,当j长度小于当前总长一半时,ans若乘上真前后缀个数,则会少一个当前s[0...j-1]这个本体..
#include <bits/stdc++.h>
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//#pragma GCC optimize("O2")
using namespace std;
#define LL long long
#define ll long long
#define ULL unsigned long long
#define ls rt<<1
#define rs rt<<1|1
#define one first
#define two second
#define MS 1000009
#define INF 1e18
#define mod 1000000007
#define Pi acos(-1.0)
#define Pair pair<LL,LL>
#define eps 1e-9
LL n,m,k;
char s[MS];
LL kp[MS];
LL ac[MS];
int main(){
ios::sync_with_stdio(false);
cin >> k;
while(k--){
cin >> s; // 字符串以0开始
LL hs = strlen(s);
kp[0] = 0;
ac[0] = 1;
for(int i=1,j=0;i<hs;i++){
while(j>0 && s[i] != s[j]) j = kp[j-1];
if(s[i] == s[j]) j++; // 前两步得出以 i 为结尾匹配的最长真前后缀的长度 j
kp[i] = j; // 记录这个长度 j
if(j) ac[i] = ac[j-1]+1; // ac[i]表示以 i 结尾 相同的前后缀的个数
else ac[i] = 1;
}
LL ans = 1;
for(int i=1,j=0;i<hs;i++){
while(j>0 && s[i] != s[j]) j = kp[j-1];
if(s[i] == s[j]) j++;
while(2*j>i+1) j = kp[j-1]; // 得出不超过长度一半的最长前后缀的长度 j
if(j) ans *= (ac[j-1]+1);
ans %= mod;
}
cout << ans << endl;
}
return 0;
}