题意:
我认为这是一道比较难的问题,自己想了很久,没有想出来怎么做,可能是因为思维僵化吧,一直在想怎么直接的由A变到B,事实上,可以有中间桥梁连接A和B,他们之间的关系可能往往没有我们想的那么简单。
依最简单的思路,我们定义ans[i]代表区间[1...i]处,A直接变到B所需的最少次数。那么状态转移方程可以这样写
有几种情况
(1)如果strA[i] == strB[i]的话,那么存在着一个转移方程就是 ans[i] = min(ans[i],ans[i-1]);
(2)否则的话,那么第i个位置的字符,一定会被刷掉,此外,刷掉第i个位置的区间长度可能大于1,假设这个区间刷掉了区间[x,i]内的数,并把区间strA[x...i]内的数都刷成了strB[i]
这样的话就相当于把区间[x...i]由空白串直接变成strB[x...i],剩下的区间[1...x-1]可以用ans[x-1]转移过来
也就是说,转移方程写成ans[i] = min(ans[i],ans[x-1] + dp[x][i]),dp[x][i]表示从空白串直接变成strB[x][i]所需要用的最小次数。
下面我们的重要目标就是求出dp[i][j]来
其实到这里还是不太好想。
最右边的字符strB[j]一定是最先被刷出来的
(1)如果strB[i] == strA[j],那么在一开始刷刷出strB[j]的时候,顺手就可以把strB[i]也给刷出来,所以不需要额外的费用
dp[i][j] = dp[i+1][j]
(2)然后我们把区间分成两块进行转移(非常常规的想法)
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]);
代码(注意我在前面的区间写的是全闭,而在代码里的区间写法是左闭右开):
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX = 106;
char strA[MAX];
char strB[MAX];
int dp[MAX][MAX];
int ans[MAX];
int n;
int main(){
while(scanf("%s %s",strA,strB) != EOF){
memset(dp,0,sizeof(dp));
memset(ans,0,sizeof(ans));
n = strlen(strA);
for(int i = 0;i < n;i++) dp[i][i+1] = 1;
for(int len = 2;len <= n;len++){
for(int i = 0;i + len <= n;i++){
dp[i][i+len] = dp[i+1][i+len] + (strB[i] == strB[i+len-1]?0:1);
for(int j = i+1;j < i+len;j++){
dp[i][i+len] = min(dp[i][i+len],dp[i][j]+dp[j][i+len]);
}
}
}
ans[0] = strA[0] == strB[0] ? 0:1;
for(int i = 1;i < n;i++){
ans[i] = dp[0][i+1];
if(strB[i] == strA[i])
ans[i] = min(ans[i],ans[i-1]);
for(int j = 0;j < i;j++){
ans[i] = min(ans[i],ans[j] + dp[j+1][i+1]);
}
}
cout<<ans[n-1]<<endl;
}
return 0;
}