原文地址:http://blog.csdn.net/a601025382s/article/details/12379565
原文作者:knownothing
String painter
Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3815 Accepted Submission(s): 1782
Problem Description
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.
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进行多少次操作,才能将a变成b。每次操作时将a中任意一段变成任意一个字母所组成的段。
题解:动态规划题。dp[i][j]表示a中i到j段变成b需要的最少次数。递推公式:dp[i][j]=min(dp[i][k]+dp[k+1][j])(i<=k<j)。接着就是判断分界点了,对于字符串b,只有将相同字符一起刷才能减少操作数。所以每次碰到b[i]==b[k]时,可以减少一次操作,因为刷一次[i,k]再刷[i+1,k-1]和分别刷[i,i][k,k],[i+,k,k+1]是一样的,可操作数会减少。
注意:由于如果一段子串两端相等,会成端更新,从而改变中间子串的字符,所以处理时可假定所以a中单个字符都需要一次变化才能变成b。之后动态规划完成后再处理a和b中形同位置相同字符的情况。
另一种理解方式:不考虑起始串,将起始串默认为空串,找出所有dp值(dp[i][j]表示i到j这段空子串转换成目标串需要的最小次数)后,再通过ans[i]来求得最小变换值。ans[i]表示前i+1长度的子串转换成目标串需要的最小次数。
耗时:15MS/2000MS
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=110;
int dp[maxn][maxn];
char a[maxn],b[maxn];
int ans[maxn];
int main()
{
while(cin>>a>>b)
{
int i,j,k,n,m,p,q,len,t;
n=strlen(a);
memset(dp,0,sizeof(dp));
//为处理方便,一个的时候都需要改变才能得到对应字母。
//因为当一段被更新后,两字符串原本相等的值就改变了。
for(i=0;i<n;i++)dp[i][i]=1;
for(len=2;len<=n;len++)//枚举长度
{
for(i=0;i<n-len+1;i++)//枚举起点
{
j=i+len-1;//终点
//cout<<i<<" "<<j<<endl;
dp[i][j]=dp[i+1][j]+1;//!!由于只有比较的是b[i]=b[k],所以不能用dp[i][j]=dp[i][j-1]+1
for(k=i+1;k<=j;k++)//枚举分割点
{
if(b[i]==b[k])
{
//cout<<i<<" "<<k<<endl;
dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]);
}
}
}
}
for(i=0;i<n;i++)
ans[i]=dp[0][i];
for(i=0;i<n;i++)
{
//重新计算两字符串中字符相同的点。
if(a[i]==b[i])
{
if(i==0)ans[i]=0;
else ans[i]=ans[i-1];
}
else
{
for(j=0;j<i;j++)
ans[i]=min(ans[i],ans[j]+dp[j+1][i]);
}
}
cout<<ans[n-1]<<endl;
}
return 0;
}