String painter
Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2580 Accepted Submission(s): 1165
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
zzzzzfzzzzz abcdefedcba abababababab cdcdcdcdcdcd
6 7
题意就是,给你两个串,每次你可以在第一个串中选择一段连续的区间,把这个区间 里每个字母都变成一个相同的字母,问你最少需要多少次操作,才能把第一个串变成第二个串。
区间dp,dp有三维,dp[l][r][k]表示区间[l,r]全被变成了字母k之后,变成字符串2所需要的最少的步骤,如果k=26的话,就表示这个区间没有改变,这个区间里所有的字母都是字符串1原来的样子,然后就是dp的状态转移了,首先我们知道,当k=26的时候,如果s1[l]==s2[l]或者s1[r]==s2[r],那么就可以直接不用考虑第l个或者第r个了,当k!=26的时候,我们就看s1[l]和s1[r]被改变之后的字母是不是就是最终我们要变得字母,要是是的话,情况就和之前说的那个一样,就可以不用考虑了。除此之外,我们还要枚举把这个区间分成两个区间分别进行改变的时候的情况,其实区间dp,这一步是很常见的,我就不多少了,最后dp[l][r][x]就等于之前说的这些情况中的最小值,最后的答案就是dp[0][len-1][26],具体的细节,看代码就好了。
我的方法和网上做的方法不一样,网上的都是二维的做法,但是代码更长,而且我觉得更难想,他们是先不管原来的串,就是把原来的串当成是空白串,算出每个区间变成目标区间所需要的最小步数,然后再接着操作,大家可以去看看,我一开始也是照着题解上的那种方法敲的,觉得好麻烦,所以我最终还是按照我自己原先的思路a了这道题,而且我觉得我的想法比较好理解一点吧,代码也比较短,下面是代码。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn = 110;
const int INF = 1e9;
int dp[maxn][maxn][30];
char s1[maxn],s2[maxn];
int slove(int l,int r,int x){
if(l>r) return 0;
if(dp[l][r][x]!=-1) return dp[l][r][x];
int ans=INF;
ans=min(slove(l+1,r,x),slove(l,r-1,x))+1;
if(x==26&&s1[l]==s2[l] || s2[l]-'a'==x) ans=min(ans,slove(l+1,r,x));
if(x==26&&s1[r]==s2[r] || s2[r]-'a'==x) ans=min(ans,slove(l,r-1,x));
if(s2[l]==s2[r]) ans=min(ans,slove(l+1,r-1,s2[l]-'a')+1);
for(int i=l;i<r;i++)
ans=min(ans,slove(l,i,x)+slove(i+1,r,x));
return dp[l][r][x]=ans;
}
int main()
{
while(scanf("%s %s",s1,s2)!=-1){
int len=strlen(s1);
memset(dp,-1,sizeof(dp));
printf("%d\n",slove(0,len-1,26));
}
return 0;
}