题解 [NOI2014] 动物园

luogu

题目大意

对于字符串 \(S\) 的前 \(i\) 个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作 num[i]。例如 \(S\)aaaaa,则num[4]=2 num[4] = 2 num[4]=2。这是因为 \(S\) 的前 \(4\) 个字符为 aaaa,其中 aaa 都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。而 aaa 虽然满足性质‘既是后缀又是前缀’,但遗憾的是这个后缀与这个前缀重叠了,所以不能计算在内。
字符串长度 \(L \le 10^6\), 多组数据 \(T \le 5\)

/*
观察:
nxt[i]: 字符串有前i个字符构成的的前缀最长的相等的真前缀和真后缀的长度 (嵌套的!)(真前/后缀:与原串不相等的前/后缀)
num[i]:字符串前i个字符不重叠的相等前后缀数量

注意到对于s[1..i]: s[1..nxt[i]], s[1..nxt[nxt[i]]], s[1..nxt[nxt[nxt[i]]]], ... 是s[1..i]的全部的相等前后缀
所以只需要不断跳nxt[i]直到不重叠即可
*/
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;

char a[maxn];
int nxt[maxn]; // KMP的nxt
int v[maxn]; // v[i]是前i个字符的前缀的前缀和后缀相同的个数(嵌套的)

int mul(int x, int y) {
	return static_cast<long long>(x) * y % mod;
}

int main() {
	int T;
	scanf("%d", &T);
	for (int _ = 0; _ < T; ++_) {
		scanf("%s", a + 1);
		int n = strlen(a + 1);

		int j = 0;
		v[1] = 1; // 初始时,只有1个字符,此时它自己的前缀和后缀都可以是它自己,故为1
		for (int i = 2; i <= n; ++i) {
			while (j > 0 && a[j + 1] != a[i]) {
				j = nxt[j];
			}
			j += a[j + 1] == a[i];

			nxt[i] = j;
			v[i] = v[j] + 1; // 递推,1是前缀i自己的贡献(一个字符串本身既是自己的前缀,又是自己的后缀)
		}

		j = 0;
		int r = 1;
		for (int i = 2; i <= n; ++i) {
			while (j > 0 && a[j + 1] != a[i]) {
				j = nxt[j];
			}
			j += a[j + 1] == a[i];

			// 题目要求前缀和后缀没有重叠部分
			// 这里j就是最大前缀(后缀)长度
			// 前后缀长度都为j,总共为2*j,而没有重叠部分要求i>=j*2
			// 当j*2>i时,通过j=nxt[j]缩短长度
			while (j * 2 > i) {
				j = nxt[j];
			}

			// 此时j长度合适,那么题目所求的num[i]显然是v[j]
			r = mul(r, v[j] + 1); // 注意题目要求+1
		}
		printf("%d\n", r);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/szdytom/p/15576369.html