周赛 万能代码 题解(区间dp)

题目链接

题目大意

给一个字符串,对其进行压缩,求压缩后长度的最小值。
压缩规则:habcabcabc 可压缩为 hMabcRR ,M表示重复串的开始,R 表示重复部分。若R前面没有M 默认 M在串的前面。

题目思路

前言

看到题啥都不会,本来以为是kmp,但是写完之后学到了区间dp的循环方法了,最外边循环逆序

正题

区间 dp, 用 dp[ i ][ j ]表示 i 到 j 的最短长度。
对于每一个 dp[ i ][ j ],可以枚举分割点 k (i<=k 例如 abcabcabc 如果分割为 abc 和 abcabc,那就可以压缩为MabcRR,但如果分割为 ab和cabcabc,那么不能压缩。
能压缩:dp[i][j]=min(dp[i][j],k-i+1+(j-k)/(k-i+1)+1);
注意 如果 i=0,则不需要 M,则不需要 +1
不能压缩:dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);

代码

#include
#include
#include
using namespace std;
const int maxn=5e1+5;
string s;
int dp[maxn][maxn];
bool check(int la,int ra,int lb,int rb){
	if((rb-lb+1)%(ra-la+1)){//长度不是倍数
		return 0; 
	}
	int len=ra-la+1;
	while(lb+len-1<=rb){//注意判断条件 
		if(s.substr(la,len)!=s.substr(lb,len)){
			return 0;
		}
		lb=lb+len;
	}
	return 1; 
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>s;
	int d=s.size();	
	for(int i=d-1;i>=0;i--){
		for(int j=0;j<=d-1;j++){
			dp[i][j]=j-i+1;
			for(int k=i;k<=j;k++){ 
				if(check(i,k,k+1,j)){
					if(i==0){
						dp[i][j]=min(dp[i][j],k-i+1+(j-k)/(k-i+1));//i==0时不需要m 
					}else{
						dp[i][j]=min(dp[i][j],k-i+1+(j-k)/(k-i+1)+1);
					}
				}else{
					dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
				} 
			} 
		}
	}
	printf("%d",dp[0][d-1]);
	return 0;
} 

你可能感兴趣的:(dp)