题目地址: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 ",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 ",dp(l,r)); } return 0; }