[BZOJ1090][SCOI2003]字符串折叠

[BZOJ1090][SCOI2003]字符串折叠

试题描述

折叠的定义如下:

  1. 一个字符串可以看成它自身的折叠。记作 \(S \Rightarrow S\)

  2. \(X(S)\)\(X(X>1)\)\(S\) 连接在一起的串的折叠。记作 \(X(S) \Rightarrow SSSS…S(X个S)\)

  3. 如果 \(A \Rightarrow A’, B \Rightarrow B’\),则 \(AB \Rightarrow A’B’\) 例如,因为 \(3(A) \Rightarrow AAA, 2(B) \Rightarrow BB\),所以 \(3(A)C2(B) \Rightarrow AAACBB\),而 \(2(3(A)C)2(B) \Rightarrow AAACAAACBB\)

给一个字符串,求它的最短折叠。例如 \(AAAAAAAAAABABABCCD\) 的最短折叠为:\(9(A)3(AB)CCD\)

输入

仅一行,即字符串 \(S\),长度保证不超过 \(100\)

输出

仅一行,即最短的折叠长度。

输入示例

NEERCYESYESYESNEERCYESYESYES

输出示例

14

数据规模及约定

见“输入

题解

\(f(l, r)\) 表示对于区间 \([l, r]\) 内的字符串最短能压缩成的长度。那么两种转移:第一种,拼接,即 \(f(l, r) = min\{f(l, k) + f(k + 1, r) | k \in [l, r)\}\);第二种,对于周期串,枚举折叠几倍。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

#define maxn 110

int n, f[maxn][maxn], fa[maxn];
char str[maxn];

int cntdig(int x) {
	int cnt = 0;
	while(x) cnt++, x /= 10;
	return cnt;
}

int main() {
	scanf("%s", str + 1);
	n = strlen(str + 1);
	
	rep(i, 1, n) rep(j, i, n) f[i][j] = j - i + 1;
	rep(len, 1, n) rep(l, 1, n - len + 1) {
		int r = l + len - 1;
		rep(i, l, r - 1) f[l][r] = min(f[l][r], f[l][i] + f[i+1][r]);
		fa[1] = fa[2] = 1;
		rep(i, 2, r - l + 1) {
			int j = fa[i];
			while(j > 1 && str[i+l-1] != str[j+l-1]) j = fa[j];
			fa[i+1] = str[i+l-1] == str[j+l-1] ? j + 1 : 1;
		}
		if(fa[r-l+2] < (r - l + 1 >> 1) + 1) continue;
//		printf("[%d, %d]\n", l, r);
//		rep(i, 1, r - l + 2) printf("%d%s", fa[i], i < r - l + 2 ? "->" : "\n");
		int tlen = r - l + 2 - fa[r-l+2], ini = tlen;
		for(; ini < r - l + 1; ini += tlen) if((r - l + 1) % ini == 0)
			f[l][r] = min(f[l][r], f[r-ini+1][r] + cntdig((r - l + 1) / ini) + 2);
//		printf("f[%d][%d] = %d\n", l, r, f[l][r]);
	}
	
	printf("%d\n", f[1][n]);
	
	return 0;
}

总感觉在哪做过这道题。

posted @ 2017-10-26 13:11  xjr01  阅读(194)  评论(0编辑  收藏  举报