[SCOI2003] 字符串折叠

[SCOI2003] 字符串折叠

区间 \(DP\) , 设 \(f[i][j]\) 表示区间 \((i, j)\) 的最短折叠长度, 然后枚举 \(k \in (i, j)\) , 判断 \((i, k)\) 是否是 \((i, j)\) 的一个循环节, 是就取 \(f[i][j] = \min (f[i][j], f[i][k] + 2 + num[(j - i + 1) / (k - i + 1)])\) , \(num[i]\) 表示 \(i\) 这个数字的字符长度. 然后每次取 \(f[i][j] = \min (f[i][j], f[i][k] + f[k + 1][j])\) .

\(code:\)

#include <bits/stdc++.h>
using namespace std;
const int N = 105;
char s[N];
int n, f[N][N], num[N];
bool Check(int l, int r, int R) {
    int len = r - l + 1, Len = R - l + 1;
    if (Len % len) return false;
    for (int i = l; i <= R; i++) {
        int j = l + (i - l) % len;
        if (s[i] != s[j]) 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++) num[i] = num[i / 10] + 1;
    for (int i = 1; i <= n; i++) f[i][i] = 1;
    for (int l = 2; l <= n; l++) {
        for (int i = 1; i + l - 1 <= n; i++) {
            int j = i + l - 1;
            f[i][j] = l;
            for (int k = i; k < j; k++) {
                if (Check(i, k, j)) f[i][j] = min(f[i][j], f[i][k] + 2 + num[l / (k - i + 1)]);
                f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j]);
            }
        }
    }
    printf("%d", f[1][n]);
    return 0;
}
posted @ 2021-08-23 08:17  sshadows  阅读(29)  评论(0编辑  收藏  举报