bzoj1090字串摺疊
阿新 • • 發佈:2019-01-01
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
NEERCYESYESYESNEERCYESYESYES
Sample Output
14
HINT
一個最短的摺疊為:2(NEERC3(YES))
沒想到居然是dp,區間dp
定義dp[i][j]表示字串[i,j]最小摺疊長度,有兩種狀態轉移的形式,如果l到r這個區間裡面不能摺疊,那就只能列舉中間點,由左右兩部分可摺疊長度相加的轉移而來
而如果本身這個區間就可以摺疊的,那假設右邊部分可以由左邊部分重複而來,那麼就是
這個f(l,i)就是左邊部分的長度,摺疊後就以這個來表示,2就是括號長度,後面的cal就是計算整個l,r區間由多少個左部分組成,就是這個數字的長度,比如14,長度就是2
程式碼:
#include <bits/stdc++.h>
using namespace std;
const int N=105;
char s[N];
//dp[i][j]表示[i,j]最小摺疊長度
int dp[N][N];
int cal(int x){
int t=0;
while(x){
t++;
x/=10;
}
return t;
}
//判斷[l,r]能否由[cl,cr]重複而成
bool judge(int l,int r,int cl,int cr){
//長度不能整除
if((r-l+1)%(cr-cl+1)!=0){
return false;
}
//列舉右部分每一個字元
for(int i=l;i<=r;i++){
//按順序判斷,注意取模
//右邊部分可能有多個左邊部分
if(s[i]!=s[(i-l)%(cr-cl+1)+cl]){
return false;
}
}
return true;
}
int solve(int l,int r){
if(l==r){
return 1;
}
if(dp[l][r]){
return dp[l][r];
}
int t=r-l+1;
//列舉斷點
for(int i=l;i<r;i++){
t=min(t,solve(l,i)+solve(i+1,r));
if(judge(i+1,r,l,i)){
//+solve(l,i)摺疊後那部分的長度
//+2兩個括號
//+get()摺疊後數字的長度
t=min(t,solve(l,i)+2+cal((r-i)/(i-l+1)+1));
}
}
return dp[l][r]=t;
}
int main(void){
scanf("%s",s);
int len=strlen(s);
printf("%d\n",solve(0,len-1));
return 0;
}