【BZOJ 3670】【NOI 2014】动物园

题意:
给出一个字符串\(S\),定义\(num\)数组的含义为\(S\)中前\(i\)个字符构成的子串中,存在一个串使得既是它的前缀又是它的后缀,并且它们不重叠,将这种串的数量记作\(num[i]\),求:

\[\begin{eqnarray*} \prod\limits_{i = l}^L (num[i] + 1) \bmod 10^9 + 7 \end{eqnarray*} \]

思路:
考虑\(ExKMP\)中的\(Next\)数组,那么对于当前\(i\),那么它贡献的范围是\(min(2(i - 1), i + Next[i] - 1)\),差分即可。

代码:

#include <bits/stdc++.h>
using namespace std;

#define N 1000010
const int p = 1e9 + 7;
char s[N];

struct ExKMP {
	int Next[N];
	int extend[N];
	//下标从1开始
	void get_Next(char *s) {
		int lens = strlen(s + 1), p = 1, pos;
		//Next[1]要特殊考虑
		Next[1] = lens;
		while (p + 1 <= lens && s[p] == s[p + 1]) ++p;
		//Next[2]初始化
		Next[pos = 2] = p - 1;
		for (int i = 3; i <= lens; ++i) {
			int len = Next[i - pos + 1];
			//第一种情况
			if (len + i < p + 1) Next[i] = len;
			//第二种情况
			else {
				//找到对于子串最靠后已经匹配的位置
				int j = max(p - i + 1, 0);
				//暴力匹配
				while (i + j <= lens && s[j + 1] == s[i + j]) ++j;
				p = i + (Next[pos = i] = j) - 1; 
			}
		}
	}

	void work(char *s, char *t) {
		get_Next(t);
		int lens = strlen(s + 1), lent = strlen(t + 1), p = 1, pos;
		while (p <= lent && s[p] == t[p]) ++p;
		p = extend[pos = 1] = p - 1;
		for (int i = 2; i <= lens; ++i) {
			int len = Next[i - pos + 1];
			if (len + i < p + 1) extend[i] = len;
			else {
				int j = max(p - i + 1, 0);
				while (i + j <= lens && j <= lent && t[j + 1] == s[i + j]) ++j;
				p = i + (extend[pos = i] = j) - 1;
			}
		}
	}
}exkmp;
int num[N];

int main() {
	int T; scanf("%d", &T);
	while (T--) {
		scanf("%s", s + 1);
		int len = strlen(s + 1);
		exkmp.get_Next(s);
		for (int i = 1; i <= len; ++i) num[i] = 0;
		for (int i = 1; i <= len; ++i) {
			if (exkmp.Next[i]) {
				int l = i, r = min(2 * (i - 1), i + exkmp.Next[i] - 1);
				++num[l];
				--num[r + 1];
			}
		}
		for (int i = 1; i <= len; ++i) num[i] += num[i - 1];
		int res = 1;
		for (int i = 1; i <= len; ++i) res = 1ll * res * (num[i] + 1) % p;
		printf("%d\n", res);
	}
	return 0;
}
posted @ 2019-07-28 09:03  Dup4  阅读(90)  评论(0编辑  收藏  举报