Description

折叠的定义如下: 1. 一个字符串可以看成它自身的折叠。记作S  S 2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S)  SSSS…S(X个S)。 3. 如果A  A’, BB’,则AB  A’B’ 例如,因为3(A) = AAA, 2(B) = BB,所以3(A)C2(B)  AAACBB,而2(3(A)C)2(B)AAACAAACBB 给一个字符串,求它的最短折叠。例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。

Input

仅一行,即字符串S,长度保证不超过100。

Output

仅一行,即最短的折叠长度。

Sample Input

NEERCYESYESYESNEERCYESYESYES

Sample Output

14

HINT

一个最短的折叠为:2(NEERC3(YES))

Source

题解

容易看出来是区间dp...

转移方程$f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]$

但是怎么处理字符串的折叠这一块很懵逼

首先一个字符串能够折叠成它的一个字串,要满足的一个很显然的条件是能够被这个子串整除,那么就可以减少掉很多无用状态的枚举了

然后对于能够整除的情况,一个一个向后暴力匹配,时间复杂度还是过得去的毕竟n那么小

注意在匹配的过程中如果有一位不相同就可以直接跳掉了

转移方程$f[l][r]=min(f[l][r],f[l][l+k-1]+2+idx((r-l+1)/k)$

$idx$是用来判断前面的数字的长度的,如果小于10返回1小于100返回2小于1000返回3

初始化就是$f[l][r]=r-l+1$但是这里会没有覆盖到单个字母的情况,所以还要再初始化一下$f[i][i]=1$

#include <bits/stdc++.h>
using namespace std;
char ch[200];
int n;
int f[200][200];
int idx(int x){
    if(x<10)return 1;
    if(x<100)return 2;
    return 3;
}
bool check(int l,int r,int len){
    for(int i=1;i<=len;i++){
        for(int j=l+i-1;j+len<=r;j+=len){
            if(ch[j]!=ch[j+len])return 0;
        }
    }
    return 1;
}
int main(){
    scanf("%s",ch+1);
    n=strlen(ch+1);
    for(int l=n;l;l--){
        f[l][l]=1;
        for(int r=l+1;r<=n;r++){
            f[l][r]=r-l+1;
            for(int k=l;k<=r;k++){
                f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);
            }
            for(int k=1;k<=r-l+1;k++){
                if((r-l+1)%k==0&&check(l,r,k)){
                    f[l][r]=min(f[l][r],f[l][l+k-1]+idx((r-l+1)/k)+2);
                }
            }
        }
    }
    printf("%d\n",f[1][n]);
    return 0;
}