题目地址:http://www.spoj.com/problems/EDIST/
题目描述:给定字符串s1,字符串s2(长度<=2000)
求将s1转换为s2最小操作数:1、添加一个字符;2、删除一个字符;3、变更一个字符
分析最优子结构:
dp[i][j]为s1串到第i个位置,s2到第j个位置时的最小布数,
则dp (i,j)= dp(i-1,j-1),s1(i)==s2(j)
min(dp(i-1,j)+1,dp(i,j-1)+1 ) ,s1(i)!=s2(j)
还是来分析前一个状态的最优子结构:
例如:s1=“asdfas”
s2=“axefsa”
如果s1(i)=s2(j),不用做任何操作。即使前面出现了“asda”,“fewaa”这种情况dp(4,5)=dp(3,4)都能保证依旧最优解因为s2最多有1个‘a’与s1的‘a’配对,
反过来想,前面dp(i-1,j-1)已经保证了最优子结构,且前面的dp(i-1,j-1)对后面的s1(i),s2(j)无影响。
s1(i)!=s2(j),则有3种选择:
dp(i)(j)=
1、s1(i)变成s2(j),dp(i-1)(j-1)+1;
2、在s1中删除s1(i),dp(i-1)(j)+1,暗含一个条件,即删除s1(i)后s1‘与s2'是相同的那么说明在dp(i-1)(j)中形成的两个串和删除s1(i)的两个串是一样的,所以和现在s1(i)无关,所以前一个最优状态是dp(i-1)(j)
3、在s1中第i个位置上添加一个s2(j),dp(i)(j-1)+1,那说明什么呢?当我们要判断s2(j)时发现s1‘上少了一个和s2(j)相同的字母。
想一想,已经没有其他可供修改的操作了,所以是在这枚举这3种后取一个最小值。
递推代码:
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
int c[2005][2005];
char s1[2005],s2[2005];
int mins(int a,int b,int c){
if (a<=b && a<=c) return a;
else if (b<=c) return b;else return c;
}
int dp(int l,int r){
c[0][0]=0;//边界处理
for(int i=1;i<=l;i++)c[i][0]=i;//表示s1要删除的个数为i
for(int j=1;j<=r;j++)c[0][j]=j;//s1要添加的个数为j
for(int i=1;i<=l;i++){
for(int j=1;j<=r;j++)
if(s1[i-1]==s2[j-1]) c[i][j]=c[i-1][j-1];//递推
else c[i][j]=mins(c[i-1][j-1]+1,c[i][j-1]+1,c[i-1][j]+1);
}
return c[l][r];
}
int main(){
// freopen("in","rb",stdin);
// freopen("out","wb",stdout);
int t;
scanf("%d",&t);
while(~scanf("%s%s",s1,s2)){
int l=strlen(s1),r=strlen(s2);
printf("%d\n",dp(l,r));
}
return 0;
}
递归代码:
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
int c[2005][2005];
int vis[2005][2005];
char s1[2005],s2[2005];
int mins(int a,int b,int c){
if (a<=b && a<=c) return a;
else if (b<=c) return b;else return c;
}
int dp(int l,int r){
int &ans=c[l][r];
if (vis[l][r]) return ans;
vis[l][r]=1;
if (l==0) return ans=r;
if (r==0) return ans=l;
if (s1[l-1]==s2[r-1]) ans=dp(l-1,r-1);
else ans=mins(dp(l,r-1)+1,dp(l-1,r)+1,dp(l-1,r-1)+1);
return ans;
}
int main(){
// freopen("in","rb",stdin);
// freopen("out","wb",stdout);
int t;
scanf("%d",&t);
while(~scanf("%s%s",s1,s2)){
int l=strlen(s1),r=strlen(s2);
memset(vis,0,sizeof(vis));
printf("%d\n",dp(l,r));
}
return 0;
}