Jzoj3443 压缩

一个文本压缩后由若干个单元组成,每个单元由3部分组成:1.正文(小写字母组成的字符串)2.若干个”*”,表示正文的又重复了几次3.单元的结尾符号”$”,显然,对于同一文本,压缩后的表示方法不唯一,但是为了方便,我们要求你采用压缩后字符串最短的压缩方法,如果有多种压缩方法,只需输出任意一种。n<=2000

看到数据范围我们发现可以用dp,设f[i][j]表示目前在第i位,最后一个循环串的长度为j的情况下,最短的长度

那么显然,无论怎么样,有一种转移总是成立的,f[i][j]=min(f[i-j][k]+j+1),这相当于新开了一个循环节

而另一个转移需要满足一个条件,f[i][j]=min(f[i-j][j]+1),当s(i-j+1,i)=s(i-j*2+1,i-j)时,这相当于在循环节里面加入一个*

那么综合下来转移就是n^3的,我们加上两个优化

1.令g[i]为min(f[i][k]),避免每次枚举k

2.判断字串相等用hash

这样我们可以将复杂度将为n^2

接下来说一下如何输出方案

先记录下s[i][j]表示状态i,j的最后一个循环节j循环次数,和上面两种转移对应,s[i][j]=1或者s[i][j]=s[i][i-j]+1

我们令d[i]表示答案为g[i]时,最后一个循环节的循环次数,令r[i]为此循环节的长度,那么求解g[i]时,就可以顺便求解d[i]和r[i]

最后输出即可

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define L long long
using namespace std;
int n,m=0; char str[2010],A[2010];
int f[2010][2010],b[2010],d[2010];
int h[2010],g[2010],s[2010][2010],r[2010];
__attribute__((optimize("-O3")))
inline L gH(int l,int r){ return h[r]-h[l-1]*b[r-l+1]; }
int main(){
	freopen("compress.in","r",stdin);
	freopen("compress.out","w",stdout);
	scanf("%s",str+1); n=strlen(str+1);
	for(int i=*b=1;i<=n;++i){
		b[i]=b[i-1]*27;
		h[i]=h[i-1]*27+str[i]-'a';
	}
	memset(g,127,sizeof g); *g=0;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=i;++j){
			f[i][j]=g[i-j]+j+1; s[i][j]=1;
			if(f[i][j]<g[i]){ d[i]=1; r[i]=j; g[i]=f[i][j]; }
			if(i>=(j<<1) && gH(i-j+1,i)==gH(i-j*2+1,i-j))
				if(f[i][j]>f[i-j][j]+1){
					s[i][j]=s[i-j][j]+1;
					f[i][j]=f[i-j][j]+1;
					if(f[i][j]<g[i])
						{ d[i]=s[i][j]; r[i]=j; g[i]=f[i][j]; }
				}
			g[i]=min(g[i],f[i][j]);
		}
	for(;n;){
		A[m++]='$';
		for(int i=1;i<d[n];++i) A[m++]='*';
		for(int i=n;i>n-r[n];A[m++]=str[i--]);
		n-=r[n]*d[n];
	}
	reverse(A,A+m); puts(A);
}


posted @ 2017-09-25 19:39  扩展的灰(Extended_Ash)  阅读(86)  评论(0编辑  收藏  举报