题目描述
给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。
压缩后的字符串除了小 写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没 有M,则从串的开始算起)开始的解压结果(称为缓冲串)。
bcdcdcdcd可以压缩为bMcdRR,另一个例子是abcabcdabcabcdxyxyz可以被压缩为abcRdRMxyRz。
输入描述:
输入仅一行,包含待压缩字符串,仅包含小写字母,长度为n。
输出描述:
输出仅一行,即压缩后字符串的最短长度。
题解
由于每次压缩的都是连续的一段,就像一个个小区间一样,所以考虑区间DP。
比较容易想到的是表示字符串的第l个字符到第r个字符的最短压缩长度。
第1种转移,就是
第2种转移,就是子串长度为偶数,并且可以拆分成两个相等的子串时的转移,即此时可以压缩。设 ,这时候转移就是
。
但上面的分析是错的。因为我们并没有考虑M字符对于串的影响。也就是说转移会受到子串区间[l,mid]的中M的限制。因为R是在匹配离他最近的M,并不是从我们区间开头开始匹配。
那么如何解决这个问题呢?我们可以多加一维限制,设置状态为
表示子串[l,r]区间中存在M字符;
表示子串[l,r]区间不存在M字串。
第1种转移为
第2种转移为,即枚举M所在的位置
第3种转移也就是子串[l,r]可以拆分成两个相等的子串[l,mid]和、[mid+1,r]的情况。此时转移:,这样就解决了M的影响。
最后答案就是。
code
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pb push_back
#define pii pair<int,int>
#define all(A) A.begin(), A.end()
#define fi first
#define se second
#define MP make_pair
#define rep(i,n) for(register int i=0;i<(n);++i)
#define repi(i,a,b) for(register int i=int(a);i<=(b);++i)
#define repr(i,b,a) for(register int i=int(b);i>=(a);--i)
template<typename T>
inline T read(){
T s=0,f=1; char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f=-1;ch = getchar();}
while(isdigit(ch)) {s=(s<<3)+(s<<1)+ch-48;ch = getchar();}
return s*f;
}
#define gn() read<int>()
#define gl() read<ll>()
template<typename T>
inline void print(T x) {
if(x<0) putchar('-'), x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
////////////////////////////////////////////////////////////////////////
const int N=2e5+100;
int dp[60][60][2];
string s;
bool check(int l,int r){
int mid=(l+r)>>1;
for(int i=l;i<=mid;++i){
if(s[i]!=s[mid+i-l+1])return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////
int main(){
cin>>s;
int n=s.length();
s="#"+s;
for(int len=1;len<=n;++len){
for(int i=1;i+len-1<=n;++i){
int j=i+len-1;
dp[i][j][0]=dp[i][j][1]=len;
for (int k=i;k<=j;++k){
dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+j-k);
dp[i][j][1]=min(dp[i][j][1],min(dp[i][k][1],dp[i][k][0])+min(dp[k+1][j][0],dp[k+1][j][1])+1);
}
if(len%2==0&&check(i,j))dp[i][j][0]=min(dp[i][j][0],dp[i][(i+j)>>1][0]+1);
}
}
print(min(dp[1][n][0],dp[1][n][1])),putchar(10);
}
/**
* In every life we have some trouble
* When you worry you make it double
* Don't worry,be happy.
**/
京公网安备 11010502036488号