当前位置: 首页 > news >正文

字符串相似度匹配算法_莱茵斯坦距离算法

package day0330;public class LevenshteinDistanceUtil {public static void main(String[] args) {String a = "WN64 F98";String b = "WN64 F98 ";System.out.println("相似度:" + getSimilarityRatio(a, b));}/*** 获取两字符串的相似度* * @param str* @param target* @return*/public static int getSimilarityRatio(String str, String target) {int max = Math.max(str.length(), target.length());System.out.println("两个字符串中最大长度:" + max);System.out.println("莱茵斯坦距离:" + compare(str, target));return Math.round((1 - (float) compare(str, target) / max) * 100);}/*** 获取莱茵斯坦距离d[n,m]* * @param str* @param target* @return*/private static int compare(String str, String target) {int d[][];// 矩阵int n = str.length();int m = target.length();int i; // 遍历str的int j; // 遍历target的char ch1;// str的char ch2;// target的int temp;// 记录相同字符在某个矩阵位置值的增量,不是O就是1if (n == 0) {return m;}if (m == 0) {return n;}d = new int[n + 1][m + 1];// 初始化第一列for (i = 0; i <= n; i++) {d[i][0] = i;}// 初始化第一行for (j = 0; j <= m; j++) {d[0][j] = j;}// 遍历strfor (i = 1; i <= n; i++) {ch1 = str.charAt(i - 1);// 去匹配targetfor (j = 1; j <= m; j++) {ch2 = target.charAt(j - 1);// 这里加32是为了不区分大小写if (ch1 == ch2 || ch1 == ch2 + 32 || ch1 + 32 == ch2) {temp = 0;} else {temp = 1;}// 左边+1,上边+1,左上角+temp取最小d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + temp);}}return d[n][m];}/*** 获取最小值* * @param one* @param two* @param three* @return*/private static int min(int one, int two, int three) {return (one = one < two ? one : two) < three ? one : three;}
}

原理

两个字符串之间的Levenshtein Distance莱文斯坦距离指的是将一个字符串变为另一个字符串需要进行编辑操作最少的次数。其中,允许的编辑操作有以下三种。

  • 「替换」:将一个字符替换成另一个字符
  • 「插入」:插入一个字符
  • 「删除」:删除一个字符

两个字符串均不为空串

当两个字符串均不为空串时,这里假设字符串A为horse、字符串B为ros进行举例分析。由于上述三种类型的操作不会改变字符串中各字符的相对顺序,故我们可以这样进行思考。每次仅对字符串A末尾进行操作,即只考虑 字符串A的前i个字符 和 字符串B的前j个字符 的莱文斯坦距离。其中。这里的i、j均为从1开始计数。则 字符串A的前5个字符 和 字符串B的前3个字符 的莱文斯坦距离lev(5,3),就是最终我们所求的字符串A、字符串B之间的莱文斯坦距离

  • 「插入」

假设我们把 horse 变为 ro 的莱文斯坦距离记为u,即:

# 字符串A的前5个字符 和 字符串B的前2个字符 的莱文斯坦距离为 u
lev(5,2) = u

则 horse 期望变为 ros,其所需的编辑次数不会超过 u+1。因为 horse 只需先经过u次编辑操作变为 ro,然后在尾部插入s字符即可变为 ros

  • 「删除」

假设我们把 hors 变为 ros 的莱文斯坦距离记为v,即:

# 字符串A的前4个字符 和 字符串B的前3个字符 的莱文斯坦距离为 v
lev(4,3) = v

则 horse 期望变为 ros,其所需的编辑次数不会超过 v+1。因为 horse 只需先进行一次删除操作变为 hors,再经过v次编辑操作即可变为 ros

  • 「替换」

假设我们把 hors 变为 ro 的莱文斯坦距离记为w,即

# 字符串A的前4个字符 和 字符串B的前2个字符 的莱文斯坦距离为 w
lev(4,2) = v

则 horse 期望变为 ros,其所需的编辑次数不会超过 w+1。因为 horse 只经过w次编辑操作即可变为 roe,然后通过一次替换操作,将尾部的e字符替换为s字符即可

至此,在这个例子中不难看出,horse、ros的莱文斯坦距离满足如下的递推公式

lev(horse, ros) = lev(5,3) = min( lev(5,2)+1, lev(4,3)+1, lev(4,2)+1 )= min(u+1, v+1, w+1)

特别地,这里对通过替换途径实现的方式做一定的说明。如果 某字符串A的第i个字符 与 某字符串B的第j个字符 完全相同,则其所需的编辑次数肯定不会超过 lev(i-1, j-1)。因为无需进行替换

通过上面的分析过程,我们其实不难看出。如果期望 字符串A的前i个字符 与 字符串B的前j个字符 完全相同。可以有如下三种途径操作方式进行实现。而最终的莱文斯坦距离就是下面三种实现方式中次数最小的一个

  1. 在 字符串A的前i个字符 与 字符串B的前j-1个字符 完全相同的基础上,进行一次**「插入」**操作
  2. 在 字符串A的前i-1个字符 与 字符串B的前j个字符 完全相同的基础上,进行一次**「删除」**操作
  3. 在 字符串A的前i-1个字符 与 字符串B的前j-1个字符 完全相同的基础上,如果字符串A的第i个字符与字符串B的第j个字符不同,则需要进行一次**「替换」**操作;如果字符串A的第i个字符与字符串B的第j个字符相同,则无需进行任何操作

推演过程

在这里插入图片描述

http://www.lryc.cn/news/249891.html

相关文章:

  • 【EI会议征稿】第九届电气、电子和计算机工程研究国际学术研讨会 (ISAEECE 2024)
  • Maven Helper插件——实现一键Maven依赖冲突问题
  • 理解位运算的规则
  • Android Bitmap 使用Vukan、RenderEffect、GLSL实现模糊
  • Vue H5页面长按保存为图片
  • 【Web】UUCTF 2022 新生赛 个人复现
  • 设置python下载包代理
  • nginx 配置前端项目添加https
  • 人群计数CSRNet的pytorch实现
  • 【HTTP协议】简述HTTP协议的概念和特点
  • 经典神经网络——AlexNet模型论文详解及代码复现
  • flutter开发实战-轮播Swiper更改Custom_layout样式中Widget层级
  • 【Flutter】graphic图表实现自定义tooltip
  • 手机上的记事本怎么打开?安卓手机通用的记事本APP
  • 一起学docker系列之十五深入了解 Docker Network:构建容器间通信的桥梁
  • 前端OFD文件预览(vue案例cafe-ofd)
  • Java[list/set]通用遍历方法之Iterator
  • ubuntu/vscode下的c/c++开发之-CMake语法与练习
  • Java(119):ExcelUtil工具类(org.apache.poi读取和写入Excel)
  • Kong处理web服务跨域
  • Kotlin学习——kt里的作用域函数scope function,let,run,with,apply,also
  • informer辅助笔记:utils/timefeatures.py
  • [Verilog语法]:===和!==运算符使用注意事项
  • mybatis 高并发查询性能问题
  • 我在Vscode学OpenCV 图像处理一(阈值处理、形态学操作【连通性,腐蚀和膨胀,开闭运算,礼帽和黑帽,内核】)
  • Yolov8实现瓶盖正反面检测
  • GAN:WGAN前作
  • 数据库应用:MongoDB 文档与索引管理
  • Python批处理PDF文件,PDF附件轻松批量提取
  • Python可迭代对象排序:深入排序算法与定制排序