最佳字符串对齐的Java实现

一段时间以来,我使用了Levenshtein distance的Apache Commons lang StringUtils实现。 它实现了一些众所周知的技巧,通过仅挂接到两个数组而不是为备忘录表分配巨大的nxm表来使用较少的内存。 它还仅检查宽度为2 * k +1的“条带”,其中k为最大编辑次数。

在levenshtein的大多数实际用法中,您只关心一个字符串是否在另一个字符串的少量编辑(1、2、3)之内。 这避免了使levenstein变得“昂贵”的大部分n * m计算。 我们发现,在ak <= 3时,具有这些技巧的levenshtein的速度比Jaro-Winkler distance快,后者是一种近似编辑距离计算,被创建为更快的近似值(这有很多原因)。

不幸的是,Apache Commons Lang实现仅计算Levenshtein,而不计算可能更有用的Damerau-Levenshtein距离 。 Levenshtein定义了编辑操作的插入,删除和替换。 Damerau变体将* transposition *添加到列表中,这对于我使用编辑距离的大多数位置都非常有用。 不幸的是,DL距离不是真正的度量标准,因为它不考虑三角形不等式,但是许多应用程序不受此影响。 从该维基百科页面可以看到,“最佳字符串对齐”和DL距离之间经常会混淆。 实际上,OSA是一种更简单的算法,并且需要较少的簿记,因此运行时间可能略微更快。

我找不到任何使用我在Apache Commons Lang中看到的内存技巧和“条带化”技巧的OSA或DL实现。 因此,我使用这些技巧实现了自己的OSA。 在某些时候,我还将使用这些技巧来实现DL,并查看性能差异是什么:

这是Java中的OSA。 它是公共领域; 随意使用。 单元测试如下。 唯一的依赖关系是Guava-,但它只是前提条件类和文档注释,因此如果您愿意,可以轻松删除该依赖关系:

package com.github.steveash.util;import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.primitives.Shorts.checkedCast;
import static java.lang.Math.abs;
import static java.lang.Math.max;import java.util.Arrays;import com.google.common.annotations.VisibleForTesting;/*** Implementation of the OSA which is similar to the Damerau-Levenshtein in that it allows for transpositions to* count as a single edit distance, but is not a true metric and can over-estimate the cost because it disallows* substrings to edited more than once.  See wikipedia for more discussion on OSA vs DL* <p/>* See Algorithms on Strings, Trees and Sequences by Dan Gusfield for more information.* <p/>* This also has a set of local buffer implementations to avoid allocating new buffers each time, which might be* a premature optimization* <p/>* @author Steve Ash*/
public class OptimalStringAlignment {private static final int threadLocalBufferSize = 64;private static final ThreadLocal<short[]> costLocal = new ThreadLocal<short[]>() {@Overrideprotected short[] initialValue() {return new short[threadLocalBufferSize];}};private static final ThreadLocal<short[]> back1Local = new ThreadLocal<short[]>() {@Overrideprotected short[] initialValue() {return new short[threadLocalBufferSize];}};private static final ThreadLocal<short[]> back2Local = new ThreadLocal<short[]>() {@Overrideprotected short[] initialValue() {return new short[threadLocalBufferSize];}};public static int editDistance(CharSequence s, CharSequence t, int threshold) {checkNotNull(s, "cannot measure null strings");checkNotNull(t, "cannot measure null strings");checkArgument(threshold >= 0, "Threshold must not be negative");checkArgument(s.length() < Short.MAX_VALUE, "Cannot take edit distance of strings longer than 32k chars");checkArgument(t.length() < Short.MAX_VALUE, "Cannot take edit distance of strings longer than 32k chars");if (s.length() + 1 > threadLocalBufferSize || t.length() + 1 > threadLocalBufferSize)return editDistanceWithNewBuffers(s, t, checkedCast(threshold));short[] cost = costLocal.get();short[] back1 = back1Local.get();short[] back2 = back2Local.get();return editDistanceWithBuffers(s, t, checkedCast(threshold), back2, back1, cost);}@VisibleForTestingstatic int editDistanceWithNewBuffers(CharSequence s, CharSequence t, short threshold) {int slen = s.length();short[] back1 = new short[slen + 1];    // "up 1" row in tableshort[] back2 = new short[slen + 1];    // "up 2" row in tableshort[] cost = new short[slen + 1];     // "current cost"return editDistanceWithBuffers(s, t, threshold, back2, back1, cost);}private static int editDistanceWithBuffers(CharSequence s, CharSequence t, short threshold,short[] back2, short[] back1, short[] cost) {short slen = (short) s.length();short tlen = (short) t.length();// if one string is empty, the edit distance is necessarily the length of the otherif (slen == 0) {return tlen <= threshold ? tlen : -1;} else if (tlen == 0) {return slen <= threshold ? slen : -1;}// if lengths are different > k, then can't be within edit distanceif (abs(slen - tlen) > threshold)return -1;if (slen > tlen) {// swap the two strings to consume less memoryCharSequence tmp = s;s = t;t = tmp;slen = tlen;tlen = (short) t.length();}initMemoiseTables(threshold, back2, back1, cost, slen);for (short j = 1; j <= tlen; j++) {cost[0] = j; // j is the cost of inserting this many characters// stripe boundsint min = max(1, j - threshold);int max = min(slen, (short) (j + threshold));// at this iteration the left most entry is "too much" so reset itif (min > 1) {cost[min - 1] = Short.MAX_VALUE;}iterateOverStripe(s, t, j, cost, back1, back2, min, max);// swap our cost arrays to move on to the next "row"short[] tempCost = back2;back2 = back1;back1 = cost;cost = tempCost;}// after exit, the current cost is in back1// if back1[slen] > k then we exceeded, so return -1if (back1[slen] > threshold) {return -1;}return back1[slen];}private static void iterateOverStripe(CharSequence s, CharSequence t, short j,short[] cost, short[] back1, short[] back2, int min, int max) {// iterates over the stripefor (int i = min; i <= max; i++) {if (s.charAt(i - 1) == t.charAt(j - 1)) {cost[i] = back1[i - 1];} else {cost[i] = (short) (1 + min(cost[i - 1], back1[i], back1[i - 1]));}if (i >= 2 && j >= 2) {// possible transposition to check forif ((s.charAt(i - 2) == t.charAt(j - 1)) &&s.charAt(i - 1) == t.charAt(j - 2)) {cost[i] = min(cost[i], (short) (back2[i - 2] + 1));}}}}private static void initMemoiseTables(short threshold, short[] back2, short[] back1, short[] cost, short slen) {// initial "starting" values for inserting all the lettersshort boundary = (short) (min(slen, threshold) + 1);for (short i = 0; i < boundary; i++) {back1[i] = i;back2[i] = i;}// need to make sure that we don't read a default value when looking "up"Arrays.fill(back1, boundary, slen + 1, Short.MAX_VALUE);Arrays.fill(back2, boundary, slen + 1, Short.MAX_VALUE);Arrays.fill(cost, 0, slen + 1, Short.MAX_VALUE);}private static short min(short a, short b) {return (a <= b ? a : b);}private static short min(short a, short b, short c) {return min(a, min(b, c));}
}
import org.junit.Testimport static com.github.steveash.util.OptimalStringAlignment.editDistance/*** @author Steve Ash*/
class OptimalStringAlignmentTest {@Testpublic void shouldBeZeroForEqualStrings() throws Exception {assert 0 == editDistance("steve", "steve", 1)assert 0 == editDistance("steve", "steve", 0)assert 0 == editDistance("steve", "steve", 2)assert 0 == editDistance("steve", "steve", 100)assert 0 == editDistance("s", "s", 1)assert 0 == editDistance("s", "s", 0)assert 0 == editDistance("s", "s", 2)assert 0 == editDistance("s", "s", 100)assert 0 == editDistance("", "", 0)assert 0 == editDistance("", "", 1)assert 0 == editDistance("", "", 100)}@Testpublic void shouldBeOneForSingleOperation() throws Exception {def a = "steve";for (int i = 0; i < 5; i++) {assertOneOp(new StringBuilder(a).insert(i, 'f'), a)assertOneOp(new StringBuilder(a).deleteCharAt(i), a)def sb = new StringBuilder(a)sb.setCharAt(i, 'x' as char);assertOneOp(sb, a)if (i > 1) {sb = new StringBuilder(a)char t = sb.charAt(i - 1)sb.setCharAt(i - 1, sb.charAt(i))sb.setCharAt(i, t)println "comparing " + sb.toString() + " -> " + aassertOneOp(sb, a)}}}@Testpublic void shouldCountTransposeAsOne() throws Exception {assert 3 == editDistance("xxsteve", "steev", 4)assert 3 == editDistance("xxsteve", "steev", 3)assert 3 == editDistance("steev", "xxsteve", 4)assert 3 == editDistance("steev", "xxsteve", 3)assert -1 == editDistance("steev", "xxsteve", 2)assert 4 == editDistance("xxtseve", "steev", 4)assert 5 == editDistance("xxtsevezx", "steevxz", 5)assert 6 == editDistance("xxtsevezx", "steevxzpp", 6)assert 7 == editDistance("xxtsfevezx", "steevxzpp", 7)assert 4 == editDistance("xxtsf", "st", 7)assert 4 == editDistance("evezx", "eevxzpp", 7)assert 7 == editDistance("xxtsfevezx", "steevxzpp", 7)}@Testpublic void shouldCountLeadingCharacterTranspositionsAsOne() throws Exception {assert 1 == editDistance("rosa", "orsa", 2)}private void assertOneOp(CharSequence a, CharSequence b) {assert 1 == editDistance(a, b, 1)assert 1 == editDistance(b, a, 1)assert 1 == editDistance(a, b, 2)assert 1 == editDistance(b, a, 2)}@Testpublic void shouldShortCutWhenSpecialCase() throws Exception {assert 1 == editDistance("s", "", 1)assert 1 == editDistance("", "s", 1)assert -1 == editDistance("s", "", 0)assert -1 == editDistance("", "s", 0)assert -1 == editDistance("st", "", 1)assert -1 == editDistance("", "st", 1)assert -1 == editDistance("steve", "ste", 0)assert -1 == editDistance("ste", "steve", 0)assert -1 == editDistance("stev", "steve", 0)assert -1 == editDistance("ste", "steve", 1)assert -1 == editDistance("steve", "ste", 1)assert 1 == editDistance("steve", "stev", 1)assert 1 == editDistance("stev", "steve", 1)}
}

参考:来自我们的JCG合作伙伴 Steve Ash 的《最佳字符串对齐的Java实现》,来自Many Cups of Coffee博客。

翻译自: https://www.javacodegeeks.com/2013/11/java-implementation-of-optimal-string-alignment.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366275.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

指令的存储与执行初探

前言导读&#xff1a;从零开始构造一台二进制加法器 假设一个处理器与存储器相连&#xff0c;存储器中存放着一些指令。这些指令通过处理器发出的寻址信号被加载到处理器中&#xff0c;这个过程称为取指令。 下面通过简单的加法运算&#xff0c;来看一下指令和数据是怎么存储在…

java 中的流_Java中的流(IO

Java中的流(IO流.java.io包中定义了多个流类型(类或抽象类)来实现 输入 / 输出功能&#xff0c;可以从不同的角度对其进行分类&#xff1a;按单位可分为&#xff1a; 字节流 (一个字节一个字节的读取) 字符流 (一个字符一个字符的读取『一个字符是两个字节』)按…

python基础学习_35岁零基础学习Python编程是否能学得会并用得上

首先&#xff0c;学习编程语言并没有年龄上的限制&#xff0c;在当前的工业互联网时代&#xff0c;不论是初级职场人还是资深职场人&#xff0c;学习一门编程语言都是有必要的&#xff0c;从这个角度来看&#xff0c;35岁开始学习编程语言是完全可以的。虽然说学习Python语言并…

css的再深入7(更新中···)

1.transparent 透明的 2.placeholder 提示语 写页面 搞清结构层次&#xff0c;保证模块化&#xff0c;让他们之间不能受到影响 (1) 元素性质 (2) 标准流 浮动带来的脱离文档流撑不起父级的高度的问题可以采用清除浮动的方式消除影响&#xff0c;absolute和fixed带来的脱…

窥探JBoss Fuse 6.1的新功能

我对即将发布的Fuse 6.1版本感到非常兴奋&#xff0c;因为有很多很棒的新功能是我真正想要的&#xff0c;而且已经有一段时间了。 HAWT工具。 因此&#xff0c;这是一个快速的先睹为快&#xff0c;主要关注于新版本的Fuse Management Console&#xff08;现已由hawtio开源项目…

[C语言] 选择排序之直接选择排序的特性及实现

[C语言] 选择排序之直接选择排序的特性及实现 1、算法特性 直接选择是一种简单、不稳定的选择排序方法&#xff0c;属于最为基础的排序方法之一。 其时间复杂度最好情况为O&#xff08;n&#xff09;、最差为O&#xff08;n&#xff09;、平均为O&#xff08;n&#xff09;&…

ubuntu 14.04 java_Ubuntu14.04下配置Java环境

Java环境配置网上已经有很多资料了&#xff0c;不过大多不适合初学者&#xff0c;Linux菜鸟更是不知所措了。。。Ubuntu14.04下配置Java环境&#xff0c;具体方案如下&#xff1a;Oracle官网上下载“jdk-8-linux-i586.tar.gz”和“jre-8-linux-i586.tar.gz”&#xff0c;放置于…

电脑测速软件_联通你我【宽带提速】让网速飞!超实用的宽带测速提速攻略来啦!...

宅在家里&#xff0c;在家娱乐、在家办公、在家上课&#xff0c;都需要畅快的宽带网络&#xff0c;所以很多小伙伴都办理了联通宽带怎样才是正确的宽带测速姿势&#xff1f;小沃给大家介绍两种简便的测速方法&#xff0c;同时说说怎样才能完全释放宽带网速、让咱享受到真正的提…

MyEclipse - 查询使用的JDK版本

查询 MyEclipse 使用的 JDK 版本 MyEclipse 菜单->Windows->Proferences->java->Installs Jres重新设置JDK路径&#xff0c;改为开发程序时的JDK版本&#xff08;如上图&#xff09;&#xff08;我做到这一步就可以了&#xff01;我的eclipse是7.0&#xff0c;jdk…

解析什么是绝对定位,相对定位与浮动

在理解定位之前&#xff0c;先说一个概念&#xff0c;文档普通流&#xff0c;就是元素标签正常在HTML里的顺序&#xff0c;块级元素从上至下排列&#xff0c;行内元素从左到右排列。 绝对定位 绝对定位&#xff1a;absolute&#xff0c;元素相对于离它最近的已定位祖先元素进…

具有多个查询参数的Camel CXF服务

出色的Apache Camel团队忙于解决查询中多个参数的处理问题&#xff0c;这是一种解决方法。 希望该帖子将在下一版本的Camel中变得过时。 &#xff08;当前&#xff0c;我使用2.7.5&#xff09; 问题 大于1的查询参数作为null值传递给Camel-CXF服务。 假设网址中有四个查询参数…

《JavaScript高级程序设计》读书笔记(三)基本概念第四小节 操作符

内容---语法 上一小节---数据类型 本小节 操作符---流程控制语句---理解函数 操作符--操作数据值的操作符--算术&#xff08;如加减&#xff09;、位操作符、关系、相等--ECMAScript操作符可以适用于很多类型的值--在用于对象时&#xff0c;会先调用对象的valueOf()和&#xff…

el-table数据不显示_数据透视表,一篇就够了

| 函数部分告一段落&#xff0c;这一篇文章助你学懂数据透视表目录&#xff1a;一、创建数据透视表二、数据透视表字段列表值的显示方式三、分析1、切片器2、计算字段3、计算项4、组合四、设计1、分类汇总2、报表布局首先&#xff0c;我们要知道应用数据透视表的前提&#xff1…

app vue 真机运行_使用 HBuilder 将 Vue 项目打包成手机 App

在开发移动端 app 时可以选择原生 app 和 webapp&#xff0c; 原生 app 体验好&#xff0c;开发成本高&#xff0c;相对的 webapp 体验要差&#xff0c;开发成本低。webapp 要求很低&#xff0c;可以说只要会开发web 站就能开发 webapp&#xff0c;这里不讨论怎么选择&#xff…

AlvinZH双掉坑里了

AlvinZH双掉坑里了 时间限制: 1000 ms 内存限制: 65536 kb题目描述 AlvinZH双掉进坑里了&#xff01; 幸运的是&#xff0c;这坑竟然是宝藏迷宫的入口。这一次AlvinZH机智地带了很多很多背包——装金币&#xff01; 假设现在AlvinZH捡到了n块金币&#xff0c;他一共带了m个背包…

解决安卓字体偏移:页面整体缩放

在h5中实现一些小标签、按钮的时候&#xff0c;很容易发现部分安卓机型上的字体显示有问题&#xff0c;总会向上偏移2px左右。这是设置padding或line-height无法修复的&#xff0c;与rem也无关&#xff0c;即使在字体大于12px时依然存在。下图来自于网友的分享&#xff0c;从左…

bmp调色板颜色信息重复_如何更改 Linux 终端颜色主题 | Linux 中国

你可以用丰富的选项来定义你的终端主题。-- Seth Kenlon如果你大部分时间都盯着终端&#xff0c;那么你很自然地希望它看起来能赏心悦目。美与不美&#xff0c;全在观者&#xff0c;自 CRT 串口控制台以来&#xff0c;终端已经经历了很多变迁。因此&#xff0c;你的软件终端窗口…

CH Round #46A 磁力块

还是一道好题的 对于一个磁石是否被吸引&#xff0c;有两个关键字&#xff1a;距离和质量。&#xff08;二维偏序&#xff1f;&#xff1f;&#xff09; 好像是很厉害的分块姿势&#xff0c;先按第一关键字排序&#xff0c;在块中按第二关键字排 进行bfs&#xff0c;对于当前磁…

CSS颜色代码

CSS颜色代码大全 FFFFFF#DDDDDD#AAAAAA#888888#666666#444444#000000#FFB7DD#FF88C2#FF44AA #FF0088 #C10066 #A20055 #8C0044 #FFCCCC#FF8888#FF3333 #FF0000 #CC0000 #AA0000 #880000 #FFC8B4#FFA488#FF7744 #FF5511 #E63F00 #C63300 #A42D00 #FFDDAA#FFBB66#FFAA33#FF8800 #…

js判断是否在iframe中

以下都可以判断代码是否在iframe中. 1.方式一 if (self.frameElement && self.frameElement.tagName "IFRAME") {   alert(在iframe中); } 2.方式二if (window.frames.length ! parent.frames.length) {   alert(在iframe中); } 3.方式三if (self ! top…