洛谷P4302 [SCOI2003]字符串折叠

题目分析
根据数据范围\(n<=100\)可以大概推出算法复杂度约为\(O(n^3)\)级别的.所以比较自然地想到区间DP.
定义\(f[i][j]\)为区间\([i,j]\)的最短折叠的长度.
初始化时,\(f[i][i]=1\),即每一个单独字符的最短折叠长度为1
考虑转移方程:
- 当前区间不考虑折叠,则枚举分界点进行转移
\(f[i][j]=min(f[i][d]+f[d+1][j])\) \((i<=d<=j)\) - 当前区间考虑折叠,则枚举循环节的长度,注意判断是否可以折叠的操作
\(f[i][j]=min(f[i][k]+2+循环次数的长度)\)(其中\(s[i][k]\)是\(s[i][j]\)的循环节,\(2\)表示一对括号的长度,注意循环次数的长度指的是这个数字的位数长度,如9的长度为\(1\), 23的长度为\(2\), 100的长度为\(3\))
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int n;
int f[N][N];
char s[N];
int count(int x) {
int res = 0;
while (x) {
res++;
x /= 10;
}
return res;
}
bool check(int l, int r, int len) {
for (int i = l; i <= r; i++)
if (s[i] != s[(i - l) % len + l]) return false;
return true;
}
int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
memset(f, 0x3f, sizeof(f));
for (int i = 1; i <= n; i++) f[i][i] = 1;
for (int k = 2; k <= n; k++)
for (int i = 1, j = i + k - 1; j <= n; i++, j++) {
for (int d = i; d < j; d++) {
f[i][j] = min(f[i][j], f[i][d] + f[d + 1][j]);
int len = d - i + 1;
if (k % len != 0) continue;
if (check(i, j, len)) f[i][j] = min(f[i][j], f[i][d] + 2 + count(k / len));
}
}
printf("%d\n", f[1][n]);
return 0;
}

浙公网安备 33010602011771号