[题解] 石家庄二中集团 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\)