String painter
Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6555 Accepted Submission(s): 3168
The first line contains string A.
The second line contains string B.
The length of both strings will not be greater than 100.
【题意】
给定两个相同长度的字符串A,B,每次操作可以使一个区间[l,r]变成同一个字符,问最少需要多少次操作时A变成B。
【分析】
我们先考虑这样一个问题:假如A是空串,A最少需要多少次操作变成B。不难想到区间dp,设dp[i][j]为将A区间[i,j]染成B的最少操作数。
有一个性质:如果两次染色的区间有交, 那么小的区间一定完全包含于大的区间(左右端点也不会重合), 且一定是大区间 在小区间之前染色(否则 小区间完全被覆盖 就没用了)。
所以第一次染色 一定是[i, k], 然后B[k + 1, j]可以单独考虑(因为染色不能再和[i, k]有交了)。
只要考虑B[i] = B[k]的位置, 如果不是,可以调整染色区域长度 变成右端点的颜色和 B[i]一样。
然后有另外一个性质:
如果B[i] = B[j], dp[i][j] = dp[i + 1][j]. 只要第一次染色区域选择[i, j], 就可以和dp[i + 1][j] 对应起来。
所以,dp[i][j]转移分为两部分:
(1)dp[i][j]=min(dp[i][j],dp[i+1][j]+(B[i]==B[i+1]?0:1) )
(2)dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]) 其中k满足B[i]==B[k] 且 i<k<=j。
再考虑A串,设f[i]表示A[0]~A[i]==B[0]~B[i]的最小操作数,初始化时假设A与B没有一个字符对应相同,即f[i]=dp[0][i] , f[0]特殊处理,f[0]=A[0]==B[0]?0:1; 那么f[i]考虑两部分,
(1) f[i]=min(f[i-1],f[i]),A[I]==B[i]
(2) f[i]=min( f[i] ,f[j] +dp[j+1][i]) 其中0<=j<i ;(可将[j+1,r]视作空串)
【代码】
#include<cstdio>
#include<cstring>
#include<iostream>
#define debug(x) cerr<<#x<<" "<<x<<'
';
using namespace std;
const int N=110;
int n,dp[N][N],f[N];char x[N],y[N];
int main(){
while(~scanf("%s%s",x+1,y+1)){
n=strlen(x+1);memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) dp[i][j]=j-i+1;
for(int j=1;j<=n;j++){
for(int i=j-1;i>=1;i--){
dp[i][j]=dp[i+1][j]+!(y[i]==y[i+1]);
for(int k=i+1;k<=j;k++){
if(y[i]==y[k]){
dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]);
}
}
}
}
for(int i=1;i<=n;i++){
f[i]=dp[1][i];
if(x[i]==y[i]){
f[i]=f[i-1];
}
else{
for(int j=1;j<i;j++){
f[i]=min(f[i],f[j]+dp[j+1][i]);
}
}
}
printf("%d
",f[n]);
}
return 0;
}