算法作业14——字符串编辑距离(期末大作业)
1. 问题
给定一个源串和目标串,能够对源串进行如下操作:
(1)在任意位置上插入一个字符;
(2)替换任意字符;
(3)删除任意字符。
例如:kitten —> sitting
操作 |
源串 |
目标串 |
替换:k —> s |
kitten |
sitten |
替换:e —> i |
sitten |
sittin |
插入:g |
sittin |
sitting |
写一个程序,实现返回最小操作次数,使得对源串进行上述这些操作后等于目标串(源串和目标串的长度都小于2000)。
2. 解析
本题我选择的求解思路是动态规划。
设s1,s2是两个字符串,其中s1是源串,s2是目标串,s1字符串长度是len1,s2字符串的长度是len2;
dp[i][j]是s1[1,2,...,i]通过插入、替换、删除得到s2[1,2,...,j]的最少操作次数,最后的dp[len1][len2]就是从s1字符串变换到s2字符串的最小操作次数。dp[i][j]的值满足以下等式:
实例:aacag —> aatat
|
空 |
a |
a |
t |
a |
t |
空 |
0 |
1 |
2 |
3 |
4 |
5 |
a |
1 |
0 |
1 |
2 |
3 |
4 |
a |
2 |
1 |
0 |
1 |
2 |
3 |
c |
3 |
2 |
1 |
1 |
2 |
3 |
a |
4 |
3 |
2 |
2 |
1 |
2 |
g |
5 |
4 |
3 |
3 |
2 |
2 |
dp表
(1)初始化:dp[0][j=0,...,s1.length]=j;
dp[i=0,...,s2.length][0]=i;
(2)i=1
s1[1]=s2[1] => dp[1][1]=dp[0][0]=0
s1[1]=s2[2] => dp[1][2]=dp[0][1]=1 插入a
s1[1]<>s2[3] => dp[1][3]=min{dp[0][2],dp[1][2],dp[0][3]}+1=2 插入a,t
s1[1]=s2[4] => dp[1][4]=dp[0][3]=3 插入a,t,a
s1[1]<>s2[5] => dp[1][5]=min{dp[0][4],dp[1][4],dp[0][5]}+1=4 插入a,t,a,t
(3)i=2
s1[2]=s2[1] => dp[2][1]=dp[1][0]=1 删除a
s1[2]=s2[2] => dp[2][2]=dp[1][1]=0
s1[2]<>s2[3] => dp[2][3]=min{dp[1][2],dp[1][3],dp[2][2]}+1=1 插入t
s1[2]=s2[4] => dp[2][4]=dp[1][3]=2 插入t,a
s1[2]<>s2[5] => dp[2][5]=min{dp[1][4],dp[1][5],dp[2][4]}+1=3 插入t,a,t
(4)i=3
s1[3]<>s2[1] => dp[3][1]=min{dp[2][0],dp[3][0],dp[2][1]}+1=2 删除a,c
s1[3]<>s2[2] => dp[3][2]=min{dp[2][1],dp[3][1],dp[2][2]}+1=1 删除c
s1[3]<>s2[3] => dp[3][3]=min{dp[2][2],dp[3][2],dp[2][3]}+1=1 替换c —> t
s1[3]<>s2[4] => dp[3][4]=min{dp[2][3],dp[3][3],dp[2][4]}+1=2 替换c —> t,插入a
s1[3]<>s2[5] => dp[3][5]=min{dp[2][4],dp[3][4],dp[2][5]}+1=3 替换c —> t,插入a,t
(5)i=4
s1[4]=s2[1] => dp[4][1]=dp[3][0]=3 删除a,a,c
s1[4]=s2[2] => dp[4][2]=dp[3][1]=2 删除a,c
s1[4]<>s2[3]=>dp[4][3]=min{dp[3][2],dp[4][2],dp[3][3]}+1=2 删除a,替换c—>t或删除c,替换a—>t
s1[4]=s2[4] => dp[4][4]=dp[3][3]=1 替换c—> t
s1[4]<>s2[5] => dp[4][5]=min{dp[3][4],dp[4][4],dp[3][5]}+1=2 替换c—> t,插入t
(6)i=5
s1[5]<>s2[1] => dp[5][1]=min{dp[4][0],dp[5][0],dp[4][1]}+1=4 删除a,c,a,g
s1[5]<>s2[2] => dp[5][2]=min{dp[4][1],dp[5][1],dp[4][2]}+1=3 删除c,a,g
s1[5]<>s2[3] => dp[5][3]=min{dp[4][2],dp[5][2],dp[4][3]}+1=3 替换c—> t,删除a,g或替换a—> t,删除c,g或替换g —> t,删除a,c
s1[5]<>s2[4] => dp[5][4]=min{dp[4][3],dp[5][3],dp[4][4]}+1=2 替换c—> t,删除g或替换g—> t,删除c
s1[5]<>s2[5] => dp[5][5]=min{dp[4][4],dp[5][4],dp[4][5]}+1=2 替换c —> t,替换g —> t
所以从源串aacag变换到目标串aatat的最小操作次数是dp[5][5]=2
3. 设计
(1)输入源串s1,目标串s2
(2)len1=s1.length(),len2=s2.length()
(3) oper()
dp[i=0,1, ... , len1][0]=i;//初始化
dp[0][j=0,1, ... ,len2]=j;//初始化
For i = 1 to len1
For j = 1 to len2
//字符相同,不操作
If s1[i-1]=s2[j-1] then dp[i][j]=dp[i-1][j-1]
//字符不同,在原有的最小操作数的基础上+1
Else dp[i][j]=min{dp[i-1][j-1],dp[i-1][j],dp[i][j-1]}+1;
Return dp[len1][len2]
(4)输出dp[len1][len2]
4.分析
如上设计,两层for循环即可解题,所以
设源串字符串长度为m,目标串长度为n,则时间复杂度=O(m*n)