串折叠 Folding

在这里插入图片描述
UVA1630
这是一个区间动态规划
定义 d p [ i ] [ j ] dp[i][j] dp[i][j] i i i j j j的字符串能压缩成的最小长度, d p s [ i ] [ j ] dps[i][j] dps[i][j]保存 i i i j j j的字符串能压缩成的最短字符串, S [ i ] S[i] S[i]表示第 i i i个字符(从1开始编号)。
初始化
d p [ i ] [ i ] = 1 d p s [ i ] [ i ] = S [ i ] dp[i][i]=1\\dps[i][i]=S[i] dp[i][i]=1dps[i][i]=S[i]
转移方程

  1. d p [ i ] [ j ] = m i n ( d p [ i ] [ k ] + d p [ k + 1 ] [ j ] ) ( i ≤ k < j ) dp[i][j]=min(dp[i][k]+dp[k+1][j])(i\leq k< j) dp[i][j]=min(dp[i][k]+dp[k+1][j])(ik<j)
    经典的区间 d p dp dp枚举中转点,那么相应的:
    i f ( d p [ i ] [ j ] > d p [ i ] [ k ] + d p [ k + 1 ] [ j ] d p s [ i ] [ j ] = d p s [ i ] [ k ] + d p s [ k + 1 ] [ j ] if(dp[i][j]>dp[i][k]+dp[k+1][j]\\dps[i][j]=dps[i][k]+dps[k+1][j] if(dp[i][j]>dp[i][k]+dp[k+1][j]dps[i][j]=dps[i][k]+dps[k+1][j]
  2. i f ( I s C i r c u l a r ( i , j , L e n ) d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i ] [ i + L e n − 1 ] + 2 + D i g i t [ S e c t i o n / L e n ] ) ( 1 ≤ L e n ≤ S e c t i o n ) if(IsCircular(i,j,Len)\\dp[i][j]=min(dp[i][j],dp[i][i+Len-1]+2+Digit[Section/Len])(1\leq Len\leq Section) if(IsCircular(i,j,Len)dp[i][j]=min(dp[i][j],dp[i][i+Len1]+2+Digit[Section/Len])(1LenSection)
    如果从 i i i开始到 j j j是一个循环节长度为 L e n Len Len的循环,那么 d p [ i ] [ j ] dp[i][j] dp[i][j]可以压缩, d p [ i ] [ i + L e n − 1 ] dp[i][i+Len-1] dp[i][i+Len1]表示从i到第一个循环节结束, + 2 +2 +2表示加上括号的长度,而 S e c t i o n Section Section为当前枚举的区间的长度,那么 S e c t i o n / L e n Section/Len Section/Len即为区间长度除以单个循环节长度得到循环节数量,而 D i g i t [ S e c t i o n / L e n ] Digit[Section/Len] Digit[Section/Len]则表示这个数量的位数,如1的位数是1,10的位数是2。那么相应的:
    i f ( d p [ i ] [ j ] > d p [ i ] [ i + L e n − 1 ] + 2 + D i g i t [ S e c t i o n / L e n ] d p s [ i ] [ j ] = t o _ s t r i n g ( S e c t i o n / L e n ) + ′ ( ′ + d p s [ i ] [ k ] + ′ ) ′ if(dp[i][j]>dp[i][i+Len-1]+2+Digit[Section/Len]\\dps[i][j] = to\_string(Section / Len) + '(' + dps[i][k] + ')' if(dp[i][j]>dp[i][i+Len1]+2+Digit[Section/Len]dps[i][j]=to_string(Section/Len)+(+dps[i][k]+)
    个数+’(’+循环节内容+’)’。
    AC代码
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
using namespace std;
string S;
int Digit[101];
int dp[101][101];
string dps[101][101];
//判断Left到Right区间是否是长度为Len的循环节的循环
bool IsCircular(const int&Left, const int& Right, const int& Len) {
	for (int i = Left; i <= Right; ++i) {
		if (S[i] != S[(i - Left) % Len + Left]) {
			return false;
		}
	}
	return true;
}
void InitDigit() {
	for (int i = 1; i <= 9; ++i) {
		Digit[i] = 1;
	}
	for (int i = 10; i <= 99; ++i) {
		Digit[i] = 2;
	}
	Digit[100] = 3;
}
int main() {
	InitDigit();
	while (cin >> S) {
		S = ' ' + S;
		memset(dp, 0x3f, sizeof(dp));
		int&& n = S.size() - 1;
		for (int i = 1; i <= n; ++i) {
			dp[i][i] = 1;
			dps[i][i] = S[i];
		}
		//枚举区间长度
		for (int Section = 2; Section <= n; Section++) {
			for (int i = 1, j = i + Section - 1; j <= n; i++, j++) {
				for (int k = i; k < j; k++) {
					if (dp[i][j] > dp[i][k] + dp[k + 1][j]) {
						dp[i][j] = dp[i][k] + dp[k + 1][j];
						dps[i][j] = dps[i][k] + dps[k + 1][j];
					}
				}
				for (int k = i; k < j; k++) {
					int&& Len = k - i + 1;
					//区间要想形成循环,只能由整数倍个循环节组成
					if (Section % Len != 0) {
						continue;
					}
					if (IsCircular(i, j, Len)) {
						if (dp[i][j] > dp[i][k] + 2 + Digit[Section / Len]) {
							dp[i][j] = dp[i][k] + 2 + Digit[Section / Len];
							dps[i][j] = to_string(Section / Len) + '(' + dps[i][k] + ')';
						}
					}
				}
			}
		}
		cout << dps[1][n] << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值