区间DP,需要学习的常用技巧是:不仅仅保存最优解的长度,还同时保存了最优解字符串,也就是说DP不仅仅是一个数组。对于i和j之间的字符,我们可以先尽量寻找他们的最小重复循环节,并且记录最小重复循环节的长度,也就得到了如果对整段进行压缩的最小长度,而后使用区间DP的中点枚举的思想,比较是直接将i到j进行压缩好,还是分开压缩好。
但是有一点需要额外注意,当发现了i-j可以直接压缩的时候,在括号里要填充dp[i][i+current_len-1].str,而不是str中i后长current_len的部分,因为可能存在嵌套压缩的情况。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct Node {
int Len;
char Str[110];
};
Node dp[110][110];
char str[110];
int L;
int main() {
scanf("%s", str);
L = strlen(str);
for(int i = 1; i <= L; i ++) {
dp[i][i].Len = 1;
dp[i][i].Str[0] = str[i - 1];
}
for(int l = 2; l <= L; l ++) {
for(int i = 1, j = i + l - 1; j <= L; i ++, j ++) {
dp[i][j].Len = 10000;
for(int k = 1; k <= l / 2; k ++) {
if(l % k != 0) {
continue;
}
int s = i, t = i + k;
while(t <= j && str[s - 1] == str[t - 1]) {
s ++;
t ++;
}
if(t > j) {
int ctr = l / k;
sprintf(dp[i][j].Str, "%d", ctr);
strcat(dp[i][j].Str, "(");
strcat(dp[i][j].Str, dp[i][i + k - 1].Str);
strcat(dp[i][j].Str, ")");
dp[i][j].Len = strlen(dp[i][j].Str);
break;
}
}
for(int k = i; k < j; k ++) {
if(dp[i][k].Len + dp[k + 1][j].Len <= dp[i][j].Len) {
dp[i][j].Len = dp[i][k].Len + dp[k + 1][j].Len;
strcpy(dp[i][j].Str, dp[i][k].Str);
strcat(dp[i][j].Str, dp[k + 1][j].Str);
}
}
}
}
printf("%s\n", dp[1][L].Str);
}