题目链接:http://acm.hust.edu.cn/vjudge/problem/51191
题意:给一个字符串,相同部分可以折叠,折叠可以嵌套。求最短长度的一种折叠方法。括号和数字的长度也要考虑进去。
思路:对于一个字符串,有三种策略:1、不折叠。2、本身可以折叠。3、分为两个区间子问题。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
const int maxn = 109;
char s[maxn];
int n;
int dp[maxn][maxn];
int type[maxn][maxn]; // 0不折叠 负数表示串自己折叠,折叠长度为值的相反数 , 正数表示分为两个区间,记录第一个区间的长度
bool flag[maxn][maxn];
int dfs(int l,int r);
int f( int x ) //计算x的位数
{
int ans = 0;
while( x ) ans++ , x/=10;
return ans;
}
int check( int l , int r , int &x ) //检查[l,r]是否可以折叠,并求出最短长度
{
int ans = 9999;
int L = r - l + 1;
rep(len,1,L-1)
if ( L % len == 0 ) //尝试len长度的折叠
{
bool flag = true;
int p1 = l , p2 = l + len;
while( p2 <= r )
{
if ( s[p1] != s[p2] ) {flag = false;break;}
p1++ , p2++;
}
if ( flag ) //可以折叠
{
int temp = f( L / len ) + 2 + dfs( l , l + len - 1 ); //包括括号和前面的次数一共的最短长度
if ( temp < ans )
{
ans = temp;
x = len; //x为循环长度
}
}
}
return ans;
}
int dfs( int L , int R )
{
if ( flag[L][R] ) return dp[L][R];
flag[L][R] = true;
if ( L == R )
{
type[L][R] = 0;
return dp[L][R] = 1;
}
int &ans = dp[L][R];
ans = R - L + 1;
type[L][R] = 0;
int x;
int temp = check(L,R,x);
if ( ans > temp )
{
ans = temp;
type[L][R] = -x;
}
int len = R - L;
rep(i,1,len)
{
temp = dfs( L , L + i - 1 ) + dfs( L + i , R );
if ( temp < ans )
{
ans = temp;
type[L][R] = i;
}
}
return dp[L][R];
}
void out( int l , int r )
{
if ( type[l][r] == 0 )
{
rep(i,l,r) putchar(s[i]);
return;
}
if ( type[l][r] < 0 )
{
printf("%d",(r-l+1)/(-type[l][r]) );
putchar('(');
out(l,l-type[l][r]-1);
putchar(')');
return;
}
out( l , l + type[l][r] - 1 );
out( l + type[l][r] , r );
}
int main()
{
while( gets(s+1) )
{
Clean(flag,false);
n = strlen(s+1);
dfs( 1 , n );
out( 1 , n );
puts("");
}
return 0;
}