String painter
题意:给你两个长度相同的字符串A, B, 要求把字符串A变为字符串B,每次只能选中一个区间[l, r], 将区间内的所有字符全部变为一种字符;问最少需要操作几次能把A转化成B;
思路:直接将A转化成B不好办,我们可以先将一个空串转化成B,再比较将A直接妆化成B那个更快;
令dp[i][j]为将[i, j]区间转换成功最少操作数;
最坏的情况下,是将每个字符都变一下,dp[i][i]=1;区间[i, i+1]是由[i, i]扩展来的,dp[i][i+1]=dp[i][i]+1;同理:dp[i][j]=dp[i][j-1]+1;然后在区间[i, j]中如果有一个B[k]==B[j]那么dp[i][j]=min(dp[i][j], dp[i][k-1]+dp[k][j-1]);这一步是如何来的呢?
如图所示[i, j]区间可以看作[i, k-1]+[k, j-1],因为B[j]==B[k],所以先[k, j]全转换成1种字符,再将[k+1, j-1]转换,相当于只转化了[k, j-1];
注意,这里不要写成dp[k+1][j-1]+1, 因为dp[k][j-1]可能更小;
下一步的工作就是看看A直接转换成B是否更快?
令ans[i]表示A的[0, i]区间转换成B的[0, i]的最少操作;
最坏情况下,A->B,相当于空串->B;ans[i]=dp[0][i];
如果A[i]==B[i] , ans[i]=ans[i-1];这一步其实也很容易理解,i处的字符相等,那么就不需要转化了,只要转化前i-1字符;需要注意的是:如果i=0,ans[i]=0;因为i-1=-1;不能做数组下标,没有ans[-1];
[0, i]转换后,看看还有没有更简便的;
ans[i]=min(ans[i], ans[k]+dp[k+1][i])(0<=k<=i);
#include <bits/stdc++.h>
using namespace std;
int main(){
char A[110], B[110];
int dp[110][110], ans[110];
while(~scanf("%s%s", A, B)){
memset(dp, 0, sizeof(dp));
int len=strlen(A);
for(int i=0; i<len; i++) dp[i][i]=1;
for(int l=2; l<=len; l++){
for(int i=0; i+l<=len; i++){
int j=l+i-1;
dp[i][j]=dp[i][j-1]+1;
for(int k=i; k<j; k++){
if(B[j]==B[k]){
dp[i][j]=min(dp[i][j], dp[i][k-1]+dp[k][j-1]);
}
}
}
}
for(int i=0; i<len; i++){
ans[i]=dp[0][i];
if(A[i]==B[i]){
if(i==0) ans[i]=0;
else ans[i]=ans[i-1];
}
for(int k=0; k<=i; k++){
ans[i]=min(ans[i], ans[k]+dp[k+1][i]);
}
}
printf("%d\n", ans[len-1]);
}
return 0;
}