【LOJ3123】「CTS2019」重复

【题目链接】

  • 点击打开链接

【思路要点】

  • 考虑计算经过无限重复后不能够找到有意义的语段的基本句子数,下称不合法的基本句子数。
  • 首先我们需要一个算法来判断某一基本句子 t t t 是否不合法。
  • t t t 重复足够多次,在 s s s k m p kmp kmp 自动机上跑,记当前节点为 p o s pos pos t t t 中下一个字符为 c c c ,若 c c c 小于 p o s pos pos f a i l fail fail 树上某一个祖先在 s s s 中的下一个字符,则 t t t 合法;若将 t t t 重复足够多次仍无法判断 t t t 合法,则 t t t 不合法。
  • 枚举将 t t t 重复足够多次后在 s s s k m p kmp kmp 自动机上匹配到的状态 p o s pos pos ,计算由 p o s pos pos m m m 步回到 p o s pos pos ,且无法判断 t t t 合法的方案数,求和即为不合法的基本句子数。
  • 直接 d p dp dp 的时间复杂度为 O ( N 2 × M ) O(N^2\times M) O(N2×M)
  • 注意到自动机上各个点不到根节点的出边若存在,是唯一的,因此可以枚举走到根节点的时刻,便只需要从根节点出发进行上述 d p dp dp
  • 时间复杂度 O ( N × M ) O(N\times M) O(N×M)

【代码】

#include
using namespace std;
const int MAXN = 2005;
const int P = 998244353;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
char s[MAXN];
int n, m, ans, dp[MAXN][MAXN];
int nxt[MAXN], trans[MAXN];
inline void update(int &x, const int &y) {
	x = (x + y >= P) ? (x + y - P) : (x + y);
}
int power(int x, int y) {
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % P;
	else return 1ll * tmp * tmp % P * x % P;
}
int main() {
	read(m);
	scanf("%s", s + 1);
	n = strlen(s + 1);
	trans[0] = 1;
	for (int i = 1; i <= n; i++) {
		int res = nxt[i - 1];
		while (res && s[res + 1] != s[i]) res = nxt[res];
		if (s[res + 1] == s[i] && i != 1) res++;
		nxt[i] = res, trans[i] = trans[res];
		if (s[i + 1] >= s[trans[res]]) trans[i] = i + 1;
	}
	dp[0][0] = 1;
	for (int i = 1; i <= m; i++)
	for (int j = 0; j <= n; j++) {
		int tmp = dp[i - 1][j];
		update(dp[i][trans[j]], tmp);
		update(dp[i][0], 1ll * tmp * ('z' - s[trans[j]]) % P);
	}
	ans = power(26, m);
	for (int i = 0; i <= n; i++) {
		int pos = i;
		for (int j = 0; j <= m - 1; j++) {
			if (pos == 0) {
				update(ans, P - dp[m - j][i]);
				break;
			}
			update(ans, P - 1ll * dp[m - j - 1][i] * ('z' - s[trans[pos]]) % P);
			pos = trans[pos];
			if (j == m - 1 && pos == i) {
				update(ans, P - 1);
			}
		}
	}
	writeln(ans);
	return 0;
}

你可能感兴趣的:(【OJ】LOJ,【类型】做题记录,【算法】自动机思想,【算法】KMP算法,【算法】动态规划)