编程之美:计算两个字符串的相似度---动态规划实现

问题描述:
把两个字符串变成相同的基本操作定义如下:
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】无后效性:动态规划法的最优解通常是由一系列最优决策组成的决策序列,最优子结构就是这些最优决策序列中的一个子序列,对于每个子序列再做最优决策会产生新的最优决策(子)序列,如果某个决策只受当前最优决策子序列的影响,而不受当前决策可能产生的新的最优决策子序列的影响,则可以理解这个最优决策具有无后效性。



  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个Java工具类,基于余弦相似度方法计算两个字符串相似度: ```java import java.util.HashMap; import java.util.Map; public class StringSimilarityUtil { /** * 计算两个字符串的余弦相似度 * * @param str1 字符串1 * @param str2 字符串2 * @return 余弦相似度 */ public static double cosineSimilarity(String str1, String str2) { // 分词 String[] words1 = str1.split(" "); String[] words2 = str2.split(" "); // 统计词频 Map<String, Integer> freq1 = new HashMap<>(); Map<String, Integer> freq2 = new HashMap<>(); for (String word : words1) { freq1.merge(word, 1, Integer::sum); } for (String word : words2) { freq2.merge(word, 1, Integer::sum); } // 计算向量长度 double length1 = 0; double length2 = 0; for (Map.Entry<String, Integer> entry : freq1.entrySet()) { length1 += Math.pow(entry.getValue(), 2); } for (Map.Entry<String, Integer> entry : freq2.entrySet()) { length2 += Math.pow(entry.getValue(), 2); } length1 = Math.sqrt(length1); length2 = Math.sqrt(length2); // 计算内积 double dotProduct = 0; for (Map.Entry<String, Integer> entry : freq1.entrySet()) { if (freq2.containsKey(entry.getKey())) { dotProduct += entry.getValue() * freq2.get(entry.getKey()); } } // 计算余弦相似度 return dotProduct / (length1 * length2); } } ``` 使用方法: ```java public static void main(String[] args) { String str1 = "Java是一种计算编程语言"; String str2 = "Java是一种面向对象的编程语言"; double similarity = StringSimilarityUtil.cosineSimilarity(str1, str2); System.out.println(similarity); // 输出:0.6666666666666667 } ``` 其中,余弦相似度计算公式为: $$ similarity = \frac{\sum_{i=1}^{n} A_i \times B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \times \sqrt{\sum_{i=1}^{n} B_i^2}} $$ 其中,$A_i$和$B_i$分别表示两个字符串中第$i$个词的频次,$n$为总词数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值