UVA1630 Folding(区间 dp)
Description
求一个字符串
S
S
S 的最短可嵌套压缩的方案。如 AAAAAAAAAABABABCCD 最短为 9(A)3(AB)CCD。
1 ≤ ∣ S ∣ ≤ 100 1 \leq |S| \leq 100 1≤∣S∣≤100。
Solution
对于一个字符串,令 f i , j f_{i,j} fi,j 为 [ i , j ] [i,j] [i,j] 的最短压缩长度,有三个压缩。
- 原串形式,将 f i , j f_{i,j} fi,j 赋初值为 j − i + 1 j-i+1 j−i+1。
- 拼接形式,枚举拼接点 k k k, f i , j = min { f i , j , f i , k + f k + 1 , j } f_{i,j} = \min \{f_{i,j}, f_{i,k} + f_{k + 1, j} \} fi,j=min{fi,j,fi,k+fk+1,j}。
- 嵌套形式,枚举嵌套的周期长度,若能嵌套,则转移,与重复次数的位数 + 2 + 2 +2 即两个括号 + + + 周期长度取 min。
因为字符串长度只有一百,所以 f 数组为 string 类型,也就 1 0 6 10^6 106。不过在枚举顺序上我遇到了一个问题,如果我们按正常顺序枚举 i i i 和 j j j,那么在枚举 k k k 进行拼接转移时, f k + 1 , j f_{k+1,j} fk+1,j 是没有处理过的。可以发现, f i , j f_{i,j} fi,j 中的 j j j 是固定的,所以可以第一维枚举 j j j,第二维枚举 i i i。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 100 + 5;
int n;
string f[N][N];
char s[N];
int count(int x) {
int ans = 0;
while (x) ans++, x /= 10;
return ans;
}
string get (int l, int r) {
string ans = "";
for (int i = l; i <= r; i++) ans += s[i];
return ans;
}
string tostr(int x) {
string ans;
while (x) ans += (x % 10) + '0', x /= 10;
reverse(ans.begin(), ans.end());
return ans;
}
int main() {
while (~scanf("%s", s + 1)) {
n = strlen(s + 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (i == j) f[i][i] = s[i];
else f[i][j] = "";
}
for (int j = 1; j <= n; j++)
for (int i = j - 1; i >= 1; i--) {
f[i][j] = get(i, j);
for (int k = i; k < j; k++)
if (f[i][j].size() > f[i][k].size() + f[k + 1][j].size()) f[i][j] = f[i][k] + f[k + 1][j];
for (int k = i; k < j; k++) {
if (s[k] == s[j] && (j - i + 1) % (k - i + 1) == 0) {
string tmp = get(i, k);
int p = k;
while (p < j && s[p + 1] == tmp[(p - i + 1) % (k - i + 1)]) p++;
if (p == j) {
if (f[i][j].size() > f[i][k].size() + count((j - i + 1) / (k - i + 1)) + 2) {
f[i][j] = tostr((j - i + 1) / (k - i + 1)) + "(" + f[i][k] + ")";
}
}
}
}
}
printf("%s\n", f[1][n].c_str());
}
return 0;
}

浙公网安备 33010602011771号