洛谷 UVA11022 String Factoring

洛谷传送门

思路

区间 dp。设 \(f_{l,r}\) 为子串 \([l,r]\) 压缩的最短长度,显然有 \(f_{l,r} \gets \min\limits_{i=l}^{r-1} f_{l,i} + f_{i+1,r}\)。还要考虑压缩 \([l,r]\) 的情况。

\(fail_{i,j}\) 为后缀 \([i,n]\) 的失配数组,跑 \(n\) 遍 KMP 即可求得。找循环节就暴力跳 \(fail\)。设当前区间为 \([l,r]\)\(fail_{l,r} = k\),当 \(r - l + 1 - k\ |\ r - l + 1\),最长循环节的长度就为 \(r - l + 1 - k\)。这样做时间复杂度为 \(O(n^3)\)。由于 \(n \le 80\),所以也可以暴力判断循环节,复杂度 \(O(n^4)\)

代码

code
/*

p_b_p_b txdy
AThousandMoon txdy
AThousandSuns txdy
hxy txdy

*/

#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second

using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;

const int maxn = 90;

int n, f[maxn][maxn], fail[maxn][maxn];
char s[maxn];

void solve() {
	n = strlen(s + 1);
	memset(f, 0x3f, sizeof(f));
	for (int i = 1; i <= n; ++i) {
		f[i][i] = 1;
	}
	memset(fail, 0, sizeof(fail));
	for (int k = 1; k <= n; ++k) {
		for (int i = k + 1, j = 0; i <= n; ++i) {
			while (j && s[i] != s[j + k]) {
				j = fail[k][j];
			}
			if (s[i] == s[j + k]) {
				++j;
			}
			fail[k][i] = j;
		}
	}
	for (int p = 2; p <= n; ++p) {
		for (int i = 1, j = p; j <= n; ++i, ++j) {
			for (int k = i; k < j; ++k) {
				f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j]);
			}
			int x = fail[i][j];
			while (x) {
				if (p % (p - x) == 0) {
					f[i][j] = min(f[i][j], f[i][i + p - x - 1]);
				}
				x = fail[i][x];
			}
		}
	}
	printf("%d\n", f[1][n]);
}

int main() {
	// int T = 1;
	// scanf("%d", &T);
	while (scanf("%s", s + 1) == 1 && s[1] != '*') {
		solve();
	}
	return 0;
}
posted @ 2022-06-22 11:00  zltzlt  阅读(34)  评论(0编辑  收藏  举报