1. 程式人生 > >BZOJ1090: [SCOI2003]字符串折疊

BZOJ1090: [SCOI2003]字符串折疊

傳送門 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,int
mid,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]字符串折疊