题目
There are two strings A and B with equal length. Both strings are made up of lower case letters. Now you have a powerful string painter. With the help of the painter, you can change a segment of characters of a string to any other character you want. That is, after using the painter, the segment is made up of only one kind of character. Now your task is to change A to B using string painter. What’s the minimum number of operations?
Input
Input contains multiple cases. Each case consists of two lines:
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
Output
A single line contains one integer representing the answer.
Sample Input
zzzzzfzzzzz
abcdefedcba
abababababab
cdcdcdcdcdcd
Sample Output
6
7
题意
给你一把小刷子,区间任选,从左到右,选择一种字母刷一遍,最后刷成第二个串的样子,问最少刷几次。
解释
由于直接考虑很复杂,先假设是空串刷成第二个串,区间dp得到dp数组再借助这个基础上,再次dp出答案。
dp[1][j]代表i~j最少的刷的次数,以i ~ j的元素作为参考系。
考虑i~j区间第一个字符的刷法,来递推。
如果第一个字符后面的字符没有与它相同的,dp[i][j] = dp[i+1][j] +1;
如果有 a[i] == a[k]条件下 dp[i][j] = min(dp[i][j], dp[i+1][k] +dp[k+1][j]);(因为有相同,所以对应的情况是,i对应的字符相当于是第一遍刷的,刷到k位置,数值上等于dp[i+1][k],这样更新完所有符合的k对应的情况,得到dp[i][j]最小值)
由于只要匹配答案肯定小于等于不匹配的情况,所以下面代码前面写出来没有分支。
按照区间dp得到空串到串2的各个区间的最小刷次数。
再考虑串1到串2借助dp数组来得到最终答案,因为串1有些部分和串2是相同的,理论上会出现可以少更新,得到更优解的可能性。
dpt[i]代表1~i的最小刷次数。
考虑1~i区间最后一个字符的刷法(考虑第一个的刷法应该也是可行的,因为刷一次时左刷还是右刷对单次产生效果不变)
如果串1第i字符与与串2第i字符相同 那么刷完1~i与刷完1 ~ i-1答案应该一样,因为i位置不需要刷,本来就相同,故 dpt[i] = dpt[i-1];
但如果不相同,dpt[i] = min(dpt[i], dpt[k]+ dp[k+1][j]);枚举所有位置.
dpt[k]+ dp[k+1][j]根据上面的解释是1~k的最小刷次数加k+1到j的最小刷次数,枚举所有位置得到最小结果。为什么是dpt[k]呢?因为1 ~ K中有些就是两串相等的,可能会有更优的结果,这也是我们二次dp的原因。本来写到这我就结束了 可是我自己问的为什么是dpt[k]?那么为什么后半段加的是dp[k+1][j],而不是dpt有关的最优答案。
从最大区间开始想,1~ i最优解等于更新过的1~k最优解加上k到i的最优解,讲道理才是1 ~ i的最优解。明显考虑了k ~i两串相同部分后的新答案必然小于等于k到i的未考虑相同部分的答案。所以代码3的写法是对的。
但是代码1的写法为什么也可以,问题是同一个区间,空串刷成串2的状态A和串一考虑相同部分不刷的的状态B,哪种更优是不一定的。
比如mbn 与 bbb 状态A反而优于B,所以我们对每个区间都要选择两者更优的选择。产生更优的只有有字符相同的子区间会比第一次区间dp可能产生更优解。所以其实不用像代码1把把所有点都枚举一遍(从某种角度上来说,代码1的状态方程思路不太对,但能更新所有的正确状态方程的情况,所以能对),因此只用对有可能产生更优解的区间作为子状态拿来更新就好了,这时候dpt存的其实是经过两次择优后的答案最优解,dpt[i] = min(dpt[i], dpt[cp[k]] + dp[cp[k]+1][i]);(cp[k]对应的值是到i之前所有相同字符的位置的值),
到这里这个问题差不多清楚了。
代码1
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 103
int const inf = 0x3f3f3f3f;
using namespace std;
int dp[maxn][maxn];
int dpt[maxn];
char str1[maxn], str2[maxn];
int main(){
int n, i, j, k;
while(~scanf("%s", str1+1)){
scanf("%s", str2+1);
n = strlen(str1+1);
memset(dp, 0, sizeof(dp));
for(i = n; i >= 1; i--){
for(j = i; j <= n; j++){
dp[i][j] = dp[i+1][j] + 1;
for(k = i+1; k <= j; k++){
if(str2[k] == str2[i])
dp[i][j] = min(dp[i][j], dp[i+1][k] + dp[k+1][j]);
}
}
}
memset(dpt, 0, sizeof(dpt));
for(i = 1; i <= n; i++){
if(str1[i] != str2[i]){
dpt[i] = dp[1][i];
for(k = 1; k < i; k++)
dpt[i] = min(dpt[i], dpt[k] + dp[k+1][i]);
}
else
dpt[i] = dpt[i-1];
}
printf("%d\n", dpt[n]);
}
return 0;
}
代码2
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 103
int const inf = 0x3f3f3f3f;
using namespace std;
int dp[maxn][maxn];
int dpt[maxn];
int cp[maxn];
char str1[maxn], str2[maxn];
int main(){
int n, i, j, k;
while(~scanf("%s", str1+1)){
scanf("%s", str2+1);
n = strlen(str1+1);
memset(dp, 0, sizeof(dp));
for(i = n; i >= 1; i--){
for(j = i; j <= n; j++){
dp[i][j] = dp[i+1][j] + 1;
for(k = i+1; k <= j; k++){
if(str2[k] == str2[i])
dp[i][j] = min(dp[i][j], dp[i+1][k] + dp[k+1][j]);
}
}
}
memset(dpt, 0, sizeof(dpt));
int cont = 1;
for(i = 1; i <= n; i++)
if(str1[i] == str2[i]){
cp[cont++] = i;
}
for(i = 1; i <= n; i++){
if(str1[i] != str2[i]){
dpt[i] = dp[1][i];
for(k = 1; k < cont && cp[k] < i; k++)
dpt[i] = min(dpt[i], dpt[cp[k]] + dp[cp[k]+1][i]);
}
else
dpt[i] = dpt[i-1];
}
printf("%d\n", dpt[n]);
}
return 0;
}
代码3
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 103
int const inf = 0x3f3f3f3f;
using namespace std;
int dp[maxn][maxn];
int dpt[maxn][maxn];
char str1[maxn], str2[maxn];
int main(){
int n, i, j, k;
while(~scanf("%s", str1+1)){
scanf("%s", str2+1);
n = strlen(str1+1);
memset(dp, 0, sizeof(dp));
for(i = n; i >= 1; i--){
for(j = i; j <= n; j++){
dp[i][j] = dp[i+1][j] + 1;
for(k = i+1; k <= j; k++){
if(str2[k] == str2[i])
dp[i][j] = min(dp[i][j], dp[i+1][k] + dp[k+1][j]);
}
}
}
memset(dpt, 0, sizeof(dpt));
for(i = n; i >= 1; i--){
for(j = 1; j <= n; j++){
if(str1[j] != str2[j]){
dpt[i][j] = dp[i][j];
for(k = i; k < j; k++)
dpt[i][j] = min(dpt[i][j], dpt[i][k] + dpt[k+1][j]);
}
else
dpt[i][j] = dpt[i][j-1];
}
}
printf("%d\n", dpt[1][n]);
}
return 0;
}