洛谷P4302 [SCOI2003]字符串折叠

题目分析

根据数据范围\(n<=100\)可以大概推出算法复杂度约为\(O(n^3)\)级别的.所以比较自然地想到区间DP.
定义\(f[i][j]\)为区间\([i,j]\)的最短折叠的长度.
初始化时,\(f[i][i]=1\),即每一个单独字符的最短折叠长度为1
考虑转移方程:

  1. 当前区间不考虑折叠,则枚举分界点进行转移
    \(f[i][j]=min(f[i][d]+f[d+1][j])\) \((i<=d<=j)\)
  2. 当前区间考虑折叠,则枚举循环节的长度,注意判断是否可以折叠的操作
    \(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;
}
posted @ 2020-10-03 21:26  lew2018  阅读(86)  评论(1)    收藏  举报
Live2D