[题解] 石家庄二中集团 3.22 日模拟赛 T1 <folding >合并字符串

题面

Bill想要进行对一串字符进行压缩,使其使用的字符长度最短。

例如, \(AAAAAAAAAABABABCCD\) 可以压缩成 \(10(A)2(BA)B2(C)D\).

\(“( )”\) 算2个字符串长度

\(n\leq100\)

题解

  • 首先一看,\(DP\) 题,而且是区间 \(DP\)

传统的 \(DP\) 在这里肯定是不行的,涉及到求前面系数的长度,因此我们肯定是要写一个 \(check\) 计算函数的。

这题的复杂度允许我们枚举循环节的长度,这两条可以合并。

int calc(int l, int r, int k) {
	for (int i = 0; i <= r - l; ++ i) {
		if (str[l + i] != str[l + i % k]) return 2147483647;
	}
	if ((r - l + 1) / k == 100) return 5 + f[l][l + k - 1];
	else if ((r - l + 1) / k >= 10) return 4 + f[l][l + k - 1];
	else return 3 + f[l][l + k - 1];
}

看完这段代码,我发现题面有 BUG

  • 看一下这段输入

      PPPPPQQQQQPPPPPQQQQQAAAAABBBBBAAAAABBBBBPPPPPQQQQQPPPPPQQQQQAAAAABBBBBAAAAABBBBBCCCCCDDDDDCCCCCDDDDD
    

我们发现括号是可以嵌套的。

因此这么写就没问题了。

总代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 110;
const int INF = 0x3f3f3f3f;
char str[maxn];
int n,f[maxn][maxn];
void CheckMin(int &X, int Y) {
	X > Y ? X = Y : 0;
}
int calc(int l, int r, int k) {
	for (int i = 0; i <= r - l; ++ i) {
		if (str[l + i] != str[l + i % k]) return 2147483647;
	}
	if ((r - l + 1) / k == 100) return 5 + f[l][l+k-1];
	else if ((r - l + 1) / k >= 10) return 4 + f[l][l+k-1];
	else return 3 + f[l][l+k-1];
}
void check(int l, int r){
	for (int i = 1; i <= r - l + 1; ++ i) {
		if ((r - l + 1) % i == 0) {
			int val = calc(l, r, i);
			CheckMin(f[l][r], val);
		}
	}
}
int main(){
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	cin>>(str+1);
	n=strlen(str+1);
	for(int len=1;len<=n;len++){
		for(int i=1;i<=n;i++){
			int j=i+len-1;
			if(j>n)break;
			f[i][j]=len;
			check(i,j);
			for(int k=i;k<j;k++){
				CheckMin(f[i][j],f[i][k]+f[k+1][j]);
			}
		}
	}
	printf("%d",f[1][n]);
	return 0;
}

后记与反思

  • 进行各种 \(DP\) 时,一定要弄清楚我要维护什么状态,多少状态

  • 理清思路再写代码,思路是最重要的。

  • 区间 DP 有时 区间长度 要从 1 开始!!!

\(return \ 0\)

posted @ 2021-08-12 16:58  ¶凉笙  阅读(73)  评论(0编辑  收藏  举报