思路:d(i, j)表示区间[i, j]字符串的最小表示方式,转移方程d(i, j) = min{d(i, k) + d(k+1, j)},用两个子串的最小表示法表示该串,但是这样会忽略自身是重复串的情况,必须加入自身重复串的判断。
AC代码
#include <cstdio>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <cstring>
#include <utility>
#include <string>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define eps 1e-10
#define inf 0x3f3f3f3f
#define PI pair<int, int>
typedef long long LL;
const int maxn = 100 + 5;
char s[maxn];
string d[maxn][maxn];
int is_repeat(int l, int r) { //[l, r]的重叠串
int n = r - l+1;
for(int i = 1; i <= n/2; ++i) {
if(n % i) continue;
int ok = 1;
for(int j = l; j+i <= r; ++j) {
if(s[j] != s[j+i]) {
ok = 0;
break;
}
}
if(ok) return i;
}
return 0;
}
void solve() {
int n = strlen(s);
for(int i = 0; i < n; ++i) {
d[i][i] = s[i];
}
for(int len = 2; len <= n; ++len) {
for(int i = 0; i <= n - len; ++i) {
int j = i + len - 1;
int cur = i, tmp = inf;
for(int k = i; k < j; ++k) {
if(d[i][k].size() + d[k+1][j].size() < tmp) {
tmp = d[i][k].size() + d[k+1][j].size();
cur = k;
}
}
d[i][j] = d[i][cur] + d[cur+1][j];
int reap = is_repeat(i, j);
if(reap) {
int cnt = len / reap;
char ch[5];
sprintf(ch, "%d", cnt);
string w = string(ch) + '(' + d[i][i+reap-1] + ')';
if(w.size() < d[i][j].size()) d[i][j] = w;
}
}
}
cout << d[0][n-1] << endl;
}
int main() {
while(scanf("%s", s) == 1) {
solve();
}
return 0;
}