BZOJ3670 - 动物园(kmp)
题目
求出一个num数组一一对于字符串S的前i个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作num[i]。
输出\(\prod_{i=1}^L(num[i]+1)\)对1,000,000,007取模的结果即可。
题解
先求出next数组。然后预处理一个tmp数组,它是num数组的后缀与前缀可以重叠的版本。
和next数组求法类似,假设位置i前的tmp数组都求出来了,那么
- tmp[0]=-1
- 当j-1或s[i]s[j]时,tmp[i]=tmp[j]+1
- 否则j=next[j],继续判断
而求num数据就是加个2*(j + 1)<=(i + 1)的限制条件,其它步骤和求tmp数组一模一样。
#include <bits/stdc++.h>
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen(".//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define pb push_back
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 1e6 + 10;
const int M = 1e9 + 7;
const double eps = 1e-5;
char s[N];
int nt[N];
int num[N];
int tmp[N];
void getnext(char t[]) {
int i = 0, j = -1;
nt[i] = num[i] = tmp[i] = j;
while(t[i]) {
if(j == -1 || t[i] == t[j]) {
i++;
j++;
nt[i] = j;
tmp[i] = tmp[j] + 1;
} else {
j = nt[j];
}
}
i = 0, j = -1;
while(t[i]) {
if(j == -1 || (t[i] == t[j] && 2 * (j + 1) <= (i + 1))) {
i++;
j++;
num[i] = tmp[j] + 1;
} else {
j = nt[j];
}
}
}
int main() {
IOS;
int t;
cin >>t;
while(t--) {
memset(num, 0, sizeof num);
cin >> s;
int len = strlen(s);
getnext(s);
int ans = 1;
for(int i = 1; i <= len; i++) {
ans = 1ll * ans * (num[i] + 1) % M;
//cout << num[i] << " ";
}
//cout << endl;
cout << ans << endl;
}
}