问题描述:
把两个字符串变成相同的基本操作定义如下:
1. 修改一个字符(如把 a 变成 b)
2. 增加一个字符 (如 abed 变成 abedd)
3. 删除一个字符(如 jackbllog 变成 jackblog)
针对于 jackbllog到jackblog 只需要删除一个或增加一个 l 就可以把两个字符串变为相同。把这种操作需要的次数定义为两个字符串的距离 L, 则相似度定义为1/(L+1) 即距离加一的倒数。那么jackbllog和jackblog的相似度为 1/1+1=1/2=0.5 也就是所两个字符串的相似度是 0.5。
给定任意两个字符串,你是否写出一个是否来计算出它们的相识度。
分析题目:本题是一个多级阶段的决策问题,每个阶段的最优选择会受到前面最优选择的影响,我们可以利用动态规划来实现,1,定义一个子问题,2,确定子问题最优解的堆叠方式。
以本题为例,假设source字符串有n个字符,target字符串有m个字符,如果将问题定义为求解将source的1-n个字符转换为target的1-m个字符所需要的最少编辑次数(最小编辑距离),则其子问题就可以定义为将source的1-i个字符转换为target的1-j个字符所需要的最少编辑次数,这就是本问题的最优子结构。我们用d[i, j]表示source[1..i]到target[1..j]之间的最小编辑距离,则计算d[i, j]的递推关系可以这样计算出来:
如果source[i] 等于target[j],则:
d[i, j] = d[i-1, j-1] + 0 (递推式 1)
如果source[i] 不等于target[j],则根据插入、删除和替换三个策略,分别计算出使用三种策略得到的编辑距离,然后取最小的一个:
d[i, j] = min(d[i, j – 1] + 1,d[i – 1, j] + 1,d[i – 1, j – 1] + 1 ) (递推式 2)
d[i, j – 1] + 1 表示对source[i]执行插入操作后计算最小编辑距离
d[i – 1, j] + 1 表示对source[i]执行删除操作后计算最小编辑距离
d[i – 1, j – 1] + 1表示对source[i]替换成target[i]操作后计算最小编辑距离
d[i, j]的边界值就是当target为空字符串(m = 0)或source为空字符串(n = 0)时所计算出的编辑距离:
m = 0,对于所有 i:d[i, 0] = i
n = 0,对于所有 j:d[0, j] = j
根据前面分析的最优子结构、最优解的递推关系以及边界值,写出用动态规划法求解最小编辑距离的算法就很容易了,以下代码就是计算两个字符串的最小编辑距离的算法实现:
public class StringSimilar {
public int fun(String source,String target){
int i,j;
int[][] d = new int[source.length()+1][target.length()+1];
for(i=1;i<source.length()+1;i++){/*初始化临界值*/
d[i][0]=i;
}
for(j=1;j<target.length()+1;j++){/*初始化临界值*/
d[0][j]=j;
}
for(i=1;i<source.length()+1;i++){/*动态规划填表*/
for(j=1;j<target.length()+1;j++){
if(source.substring(i-1, i).equals(target.substring(j-1, j))){
d[i][j]=d[i-1][j-1];/*source的第i个和target的第j个相同时*/
}else{/*不同的时候则取三种操作最小的一个*/
d[i][j]=min(d[i][j-1]+1,d[i-1][j]+1,d[i-1][j-1]+1);
}
}
}
return d[source.length()][target.length()];
}
private int min(int i, int j, int k) {
int min = i<j?i:j;
min = min<k?min:k;
return min;
}
public static void main(String[] args) {
StringSimilar ss = new StringSimilar();
System.out.println(ss.fun("SNOWY", "SUNNY"));//3
System.out.println(ss.fun("a", "b"));//1
System.out.println(ss.fun("abdd", "aebdd"));//1
System.out.println(ss.fun("travelling", "traveling"));//1
}
}
总结:
注解:
【1】最优子结构:对于多阶段决策问题,如果每一个阶段的最优决策序列的子序列也是最优的,且决策序列具有“无后效性”,就可以将此决策方法理解为最优子结构。
【2】无后效性:动态规划法的最优解通常是由一系列最优决策组成的决策序列,最优子结构就是这些最优决策序列中的一个子序列,对于每个子序列再做最优决策会产生新的最优决策(子)序列,如果某个决策只受当前最优决策子序列的影响,而不受当前决策可能产生的新的最优决策子序列的影响,则可以理解这个最优决策具有无后效性。