原题:
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
中文:
给你两个字符串a和字符串b,你有一个刷子,一次可以将一个连续区间的字符串刷成同一种字符,限制问你最少刷多少次可以将字符串a刷成b。
例如第一个给的样例
zzzzzfzzzzz
abcdefedcba
要求将字符串zzzzzfzzzzz刷成abcdefedcba
第一步可以刷成这样
aaaaaaaaaaa
第二步
abbbbbbbbba
第三步
abcccccccba
第四步
abcdddddcba
第五步
abcdeeedcba
第六步
abcdefedcba
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100 + 10;
char s1[maxn],s2[maxn];
int dp[maxn][maxn];
int ans[maxn];
int main()
{
ios::sync_with_stdio(false);
while(cin>>&s1[1])
{
cin>>&s2[1];
memset(dp,0,sizeof(dp));
memset(ans,0,sizeof(ans));
int len = strlen(&s1[1]);
for(int R = 1;R<=len ;R++)
{
for(int L = R; L>=1; L--)
{
dp[L][R] = dp[L+1][R]+1;
for(int k = L + 1; k<=R ;k++)
{
if(s2[L] == s2[k])
dp[L][R] = min(dp[L][R], dp[L+1][k] + dp[k+1][R]);
}
}
}
for(int i = 1;i<=len;i++)
{
ans[i]=dp[1][i];
}
for(int i = 1;i<=len;i++)
{
if(s1[i] == s2[i])
ans[i] = ans[i-1];
else
{
for(int j=1;j<=i;j++)
ans[i]=min(ans[i],dp[j+1][i] + ans[j]);
}
}
cout<<ans[len]<<endl;
}
return 0;
}
解答:
数据上来说,字符串长度最多只有100。这意味着要求时间复杂度要在O(n^3)以内完成。
按照给的样例,手动模拟一下不难发现,如果单纯利用区间dp的模型来考虑,每次进行状态转移的的方法按照区间来考虑,状态转移方程如下:
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
]
[
x
−
1
]
+
p
a
i
n
t
(
x
,
y
)
+
d
p
[
y
+
1
]
[
j
]
)
dp[i][j] = min(dp[i][j],dp[i][x-1] + paint(x,y) + dp[y+1][j] )
dp[i][j]=min(dp[i][j],dp[i][x−1]+paint(x,y)+dp[y+1][j])
其中paint(x,y)表示把a字符串涂成b字符串,这里有个问题,paint(x,y)函数怎么实现?
如果要将区间[x,y]涂成字符串b对应的字符串,那么如何处理这个区间才能使paint函数最优?
这样又回到了原来的问题。
所以,此题不好直接在区间上考虑。
考虑将一个空字符串涂抹成字符串b所需要的最少涂抹次数,设置状态dp[i][j]表示将空字符串区间[i,j]涂抹成字符串b区间[i,j]的字符串所需要的最少涂抹次数:
状态转移方程表示为:
初始
d
p
[
i
]
[
j
]
=
d
p
[
i
+
1
]
[
j
]
+
1
dp[i][j]= dp[i+1][j] + 1
dp[i][j]=dp[i+1][j]+1表示可以将第i个字符单独涂成字符串b[i]对应的字符,剩下的[i+1,j]的字符使用dp[i+1][j]的方式涂上。
如果在字符串b中有b[i]==b[k],其中k∈[i+1,j]
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
+
1
]
[
k
]
+
d
p
[
k
+
1
]
[
j
]
)
dp[i][j] = min(dp[i][j],dp[i+1][k] + dp[k+1][j])
dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j])
表示如果b[i]字符与b[k]字符相同,那么可以先将区间[i,k]一次涂抹成字符b[i]的形式,再对[i+1,k]的字符进行操作
看了几个博客,下面有一些评论:
如果在
b
[
i
]
=
=
b
[
k
]
b[i]==b[k]
b[i]==b[k]时
d
p
[
i
+
1
]
[
k
]
dp[i+1][k]
dp[i+1][k]表示将区间[i,k]涂抹成字符串b[k],为什么不可以表达成下面这样的形式?
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
+
1
]
[
k
]
+
1
+
d
p
[
k
+
1
]
[
j
]
)
dp[i][j] = min(dp[i][j],dp[i+1][k]+1 + dp[k+1][j])
dp[i][j]=min(dp[i][j],dp[i+1][k]+1+dp[k+1][j])
或
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
+
1
]
[
k
−
1
]
+
1
+
d
p
[
k
+
1
]
[
j
]
)
dp[i][j] = min(dp[i][j],dp[i+1][k-1] + 1 + dp[k+1][j])
dp[i][j]=min(dp[i][j],dp[i+1][k−1]+1+dp[k+1][j])
第一个状态转移方程不能+1的原因在于,如果 b [ i ] = = b [ k ] b[i]==b[k] b[i]==b[k]那么你要涂抹区间[i,k]时,也就是再 d p [ i ] [ k ] dp[i][k] dp[i][k]这个状态的中,以及涂抹成了b[i]了(加过1了),如果再加1,那就重复。
第二个方程不能写成 d p [ i + 1 ] [ k − 1 ] + 1 dp[i+1][k-1] + 1 dp[i+1][k−1]+1形式,因为是第b[i]与b[k]相同,表示字符串[i,k]已经涂抹成了b[i]或b[k]表示的状态, d p [ i + 1 ] [ k ] dp[i+1][k] dp[i+1][k],表示你刷了第k个字符。
可以画个3*3的表,计算一下aba这个数据