1090: [SCOI2003]字符串折叠

 

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))

 

这道题,是个区间dp。

动态转移方程不大好写,但思路可以很清晰。

找到边界l和r,再枚举一个中间点,看能否进行折叠。

区间DP
f[i][j]表示i~j的最小值
状态转移:f[i][j]=min(j-i+1,f[i][k]+f[k+1][j])
if(能合体) f[i][j]=min(f[i][j],f[i][k]+倍数的长度+2(括号长度))

清晰的思路在代码中。

#include<cstdio>
#include<iostream>
#include<cstring>
#define N 110
using namespace std;
int f[N][N],n;char s[N];
bool ok(int x1,int y1,int x2,int y2){//[x1,y1][x2,y2]分别是左右两半区间 
    int t=(y2-x2+1)/(y1-x1+1),le=y1-x1+1;//t=len2/len1,le=len1 
    if((y2-x2+1)%(y1-x1+1))return false;//两者连长度都不成比例,更不会有关系 
    for(int i=1;i<=t;i++)//第二段区间的长度是第一段区间长度的整t倍 
        for(int j=1;j<=le;j++)//枚举第一段区间的所有位 
            if(s[x1+j-1]!=s[(i-1)*le+x2+j-1])return false;//依次检验是否匹配 
    return true;
}
int get(int t){
    int p=0;
    while(t){t/=10;p++;}
    return p;
}
int dfs(int l,int r){
    if(l==r)return 1;
    if(l>r)return 0;
    if(f[l][r])return f[l][r];
    int t=r-l+1;//这段区间的长度 
    for(int i=l;i<r;i++){//枚举分割点 
        t=min(dfs(l,i)+dfs(i+1,r),t);
        if(ok(l,i,i+1,r))t=min(t,dfs(l,i)+2+get((r-i)/(i-l+1)+1));
        //如果符合条件,得到一个目前最短长度 
        //+1是因为要加上左侧一字符串 
    }
    return f[l][r]=t;
}
int main(){
    //freopen("jh.txt","r",stdin);
    scanf("%s",s+1);n=strlen(s+1);
    printf("%d",dfs(1,n));
    return 0;
}