计算两个字符串的相似度---动态规划实现

问题描述:
把两个字符串变成相同的基本操作定义如下:
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
 
        根据前面分析的最优子结构、最优解的递推关系以及边界值,写出用动态规划法求解最小编辑距离的算法就很容易了,以下代码就是计算两个字符串的最小编辑距离的算法实现:

 
  1. public class Main{  
  2.     public int fun(String source,String target){  
  3.         int i,j;  
  4.         int[][] d = new int[source.length()+1][target.length()+1];  
  5.         for(i=1;i<source.length()+1;i++){/*初始化临界值*/  
  6.             d[i][0]=i;  
  7.         }  
  8.         for(j=1;j<target.length()+1;j++){/*初始化临界值*/  
  9.             d[0][j]=j;  
  10.         }  
  11.         for(i=1;i<source.length()+1;i++){/*动态规划填表*/  
  12.             for(j=1;j<target.length()+1;j++){  
  13.                 if(source.substring(i-1, i).equals(target.substring(j-1, j))){  
  14.                     d[i][j]=d[i-1][j-1];/*source的第i个和target的第j个相同时*/  
  15.                 }else{/*不同的时候则取三种操作最小的一个*/  
  16.                     d[i][j]=min(d[i][j-1]+1,d[i-1][j]+1,d[i-1][j-1]+1);  
  17.                 }  
  18.             }  
  19.         }  
  20.         return d[source.length()][target.length()];  
  21.     }  
  22.     private int min(int i, int j, int k) {  
  23.         int min = i<j?i:j;  
  24.         min = min<k?min:k;  
  25.         return min;  
  26.     }  
  27.     public static void main(String[] args) {  
  28.         StringSimilar ss = new StringSimilar();  
  29.         System.out.println(ss.fun("SNOWY", "SUNNY"));//3  
  30.         System.out.println(ss.fun("a", "b"));//1  
  31.         System.out.println(ss.fun("abdd", "aebdd"));//1  
  32.         System.out.println(ss.fun("travelling", "traveling"));//1  
  33.     }  
  34. }  

总结:

注解:
【1】最优子结构:对于多阶段决策问题,如果每一个阶段的最优决策序列的子序列也是最优的,且决策序列具有“无后效性”,就可以将此决策方法理解为最优子结构。
 
【2】无后效性:动态规划法的最优解通常是由一系列最优决策组成的决策序列,最优子结构就是这些最优决策序列中的一个子序列,对于每个子序列再做最优决策会产生新的最优决策(子)序列,如果某个决策只受当前最优决策子序列的影响,而不受当前决策可能产生的新的最优决策子序列的影响,则可以理解这个最优决策具有无后效性。

原文地址:https://www.cnblogs.com/wwjldm/p/7102156.html