BZOJ1090: [SCOI2003]字符串折疊
阿新 • • 發佈:2017-12-12
傳送門 i++ lin memset scoi2003 online ble aabb namespace
【傳送門:BZOJ1090】
簡要題意:
給出一個字符串,可以將相鄰的重復的子串合並在一起,如:abaaaabba,可以合並為ab4(a)bba
註意,數字和括號均算作字符,數字有多少位就相當於有多少個字符
請問怎麽合並才能使字符串的長度最小(也可以不合並)
題解:
區間DP,本來想著會T,結果應該是不會詢問這麽多遍,所以耗時可觀
f[i][j]表示第i個字符到第j個字符所組成的子串合並後的最短長度
一般情況下f[i][j]=min(f[i][j],f[i][k]+f[k+1][j])
如果可以合並呢?
那我們就枚舉將要合並的子串長度,然後找開頭結尾,然後判斷是否為循環串,如果是的話,就加多一步操作:
f[i][j]=min(f[i][j],f[i][k]+2+calc(i,k,j))calc表示這個循環節循環的次數的位數
最後輸出f[1][n]就行了,n表示整個串的長度
參考代碼:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; char st[110]; int f[110][110]; bool check(int l,intmid,int r) { if((r-mid)%(mid-l+1)!=0) return false; int len=mid-l+1; for(int i=mid+1;i<=r;i++) { int t=(i-mid)%mid; if(t==0) t=mid; if(st[i]!=st[t+l-1]) return false; } return true; } int calc(int l,int mid,int r) { int t=(r-mid)/(mid-l+1)+1; int tt=0; while(t!=0){t/=10;tt++;} return tt; } int main() { scanf("%s",st+1); int n=strlen(st+1); memset(f,63,sizeof(f)); for(int i=1;i<=n;i++) f[i][i]=1; for(int s=2;s<=n;s++) { for(int i=1;i+s-1<=n;i++) { int j=i+s-1; for(int k=i;k<j;k++) { f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]); if(check(i,k,j)==true) f[i][j]=min(f[i][j],f[i][k]+2+calc(i,k,j)); } } } printf("%d\n",f[1][n]); return 0; }
BZOJ1090: [SCOI2003]字符串折疊