1090: [SCOI2003]字符串折叠
Description
折叠的定义如下: 1. 一个字符串可以看成它自身的折叠。记作S S 2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) SSSS…S(X个S)。 3. 如果A A’, BB’,则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
Sample Output
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; }