#1320 : 壓縮字符串
描述
小Hi希望壓縮一個只包含大寫字母'A'-'Z'的字符串。他使用的方法是:如果某個子串 S 連續出現了 X 次,就用'X(S)'來表示。例如AAAAAAAAAABABABCCD可以用10(A)2(BA)B2(C)D表示。
此外,這種壓縮方法是可以嵌套的,例如HIHOHIHOCODERHIHOHIHOCODER可以表示成2(2(HIHO)CODER)。
對於一個字符串 S ,合法的壓縮表示可能有很多種。例如AAAAAAAAAABABABCCD還可以表示成9(A)3(AB)CCD。小Hi希望知道其中最短的表示方法長度是多少。
輸入
第一行一個正整數 T (1 ≤ T ≤ 10),表示測試數據的組數。
以下 T 行每行一個字符串 S ,長度不超過100。
輸出
對於每組數據,輸出最短的表示方法的長度。
一開始看這道題沒看出來是動態規劃,只能說自己還是太菜了。
官方題解:
本題是一道非常經典的動態規劃題目。
設S[1..n]是一個長度爲n的字符串,best(S)是S的最短壓縮,那麼best(S)可能爲三種形式中最短的一種:
1) 原串形式:best(S) = S。例如CCD最短壓縮就是CCD本身。
2) 拼接形式: best(S[1..n]) = best(S[1..i]) + best(S[i+1 .. n])。
例如AAAAAAAAAABABABCCD的最短壓縮9(A)3(AB)CCD,可以視爲由best(AAAAAAAAAABABAB) = 9(A)3(AB) 和 best(CCD) = CCD 拼接而成。
3) 嵌套形式: best(S[1..n]) = k的位數 + 2 + best(S[1..n/k]),其中k>1且是n的約數,S是由k個S[1..n/k]循環拼接而成。
也就是說S[1..n]可以表示成k(s[1..n/k]),這時k(s[1..n/k])的長度是k的位數 + 一對括號的長度2 + best(S[1..n/k)
例如HIHOHIHOCODERHIHOHIHOCODER有循環節HIHOHIHOCODER,所以best(HIHOHIHOCODERHIHOHIHOCODER) = 1 + 2 + (best(HIHOHIHOCODER))。
思路就是這樣,具體我們可以枚舉最大的長度從2...len,最小就是1,但是初始化可以設爲1,所以從而枚舉,最大就是本身的長度。
枚舉長度之後就枚舉起始位置,結束位置就是 起始位置+長度。然後取上述三種情況中的最小值。注意邊界條件,就是for數組的區間,還有長度等變量。(字符串的一大坑點)
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int ma=105,INF=0x3f3f3f3f;
char s[ma];
int dp[ma][ma];
int cal(int b,int t)
{
bool flag;
int l=t-b+1;
for(int i=1; i<=l/2; ++i)
{
flag=true;
for(int j=b+i; j<=t; j+=i)
{
for(int k=0; k<i; ++k)
{
if(s[b+k]!=s[j+k])
{
flag=false;
break;
}
}
if(!flag) break;
}
if(flag) return i;
}
return -1;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",s);
int len=strlen(s);
memset(dp,INF,sizeof(dp));
for(int i=0; i<len; ++i) dp[i][i]=1;
for(int i=2; i<=len; ++i)
{
for(int j=0; j<len; j++)
{
int r=j+i-1;
if(r>=len) break;
dp[j][r]=min(dp[j][r],i);
for(int k=j; k<r; ++k)
dp[j][r]=min(dp[j][r],dp[j][k]+dp[k+1][r]);
int pos=cal(j,r);
if(pos<0) continue;
dp[j][r]=min(dp[j][r],(i/pos<10?1:2)+2+dp[j][j+pos-1]);
}
}
printf("%d\n",dp[0][len-1]);
}
return 0;
}