题目描述
给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。
压缩后的字符串除了小 写字母外还可以(但不必)包含大写字母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. **/