bzoj1090[SCOI2003]字符串折叠
题意:
折叠的定义如下:1. 一个字符串可以看成它自身的折叠。记作S。2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S)。注意括号可以嵌套。给出字符串,求折叠后字符串的最短长度。
字符串长度≤100。
题解:
区间dp。f[i][j]=min(f[i][k]+f[k+1][j],f[i][k]+len(getnum(i,k,i,j)))。f[i][j]表示i到j的最短折叠长度。getnum(i,k,i,j)表示字符串[i..k]在字符串[i..j]中出现了几次(如果后者不是前者重复组成的返回正无穷),这个可以用哈希求。len表示一个数的长度,注意正无穷的长度为正无穷。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 110 6 #define ll unsigned long long 7 #define zsye 2333 8 #define INF 0x3fffffff 9 using namespace std; 10 11 int f[maxn][maxn],n; ll hash[maxn],mi[maxn]; 12 char str[maxn]; 13 ll get(int l,int r){ 14 return hash[r]-hash[l-1]*mi[r-l+1]; 15 } 16 int getnum(int l1,int r1,int l2,int r2){ 17 if((r2-l2+1)%(r1-l1+1)!=0)return INF; ll orzzs=get(l1,r1); 18 inc(i,1,(r2-l2+1)/(r1-l1+1))if(orzzs!=get(l2+(i-1)*(r1-l1+1),l2+i*(r1-l1+1)-1))return INF; 19 return (r2-l2+1)/(r1-l1+1); 20 } 21 int numlen(int x){if(x==INF)return INF; int tot=0; while(x)x/=10,tot++; return tot;} 22 void dfs(int l,int r){ 23 if(f[l][r]!=-1)return; inc(i,l,r-1)dfs(l,i),dfs(i+1,r); f[l][r]=INF; 24 inc(i,l,r-1)f[l][r]=min(f[l][r],f[l][i]+f[i+1][r]); 25 inc(i,l,r)f[l][r]=min(f[l][r],f[l][i]+2+numlen(getnum(l,i,l,r))); 26 } 27 int main(){ 28 scanf("%s",str+1); n=strlen(str+1); mi[0]=1; 29 inc(i,1,n)hash[i]=hash[i-1]*zsye+str[i],mi[i]=mi[i-1]*zsye; 30 memset(f,-1,sizeof(f)); inc(i,1,n)f[i][i]=1; 31 dfs(1,n); printf("%d",f[1][n]); return 0; 32 }
20161110