单词错误纠正功能 编辑距离 最大公共字串 两个字符串的相似度 差异度

莱文氏距离

i指向a字符串中的字符j指向b字符串中的字符s[][]存储第a中第i个字符和b中第j个字符相比较后,最小的莱文氏距离值

状态的转移对应2种,其中字符不相等时有3种情况1.a[i]==b[j] ,那么 i++ j++ ,距离值保持上一状态的2.a[i]!=b[j] ,需要增加距离值,那么需要考虑是下面3种情况的哪一种(能得到最小的距离值增量,所以需要做穷举组合处理)  2.1. i++ (表示a中当前字母当删除处理,或者在b中虚拟添加一个和a[i]一样的字符,处理后,距离值+1,然后i自增指向到下一个a中字符)  2.2. j++ (和 上例相同,不过是对b处理,或对a中添加一个一样的字符)  2.3. i++ j++ (将a中的i个字符替换成和b中j一样的,或这 b中的j替换成和a中的i一样(不用真的去操作,只需要距离值+1,然后移动i,j就可以了),距离值需要+1,然后i,j各自增1)

状态转移公式 s[i][j] = min(s[i-1][j-1],s[i-1][j],s[i][j-1]) + (a[i]==b[j] ? 0:1 );
//2个字符串之间的编辑距离
//1.莱文氏距离(不同字符的编辑最少编辑次数)
//2.最长公共子串(相同公共字符的最大长度)???
public class EditDst {
    //莱文氏距离
    //逐个比较a,b2个字符串中的字符是否相等
    //若不相等,那么通过往a或b中虚拟的进行 新增字符,删除字符,替换为相同字符 这3个操作,将指向a的下标i,或指向b的下标j,向后直接略过1位字符(此时需要增加编辑次数 +1)
    //比较完所有字符后,将所需编辑的最小次数作为莱文氏距离返回

    //动态规划,莱文氏距离
    //时间 O(n*w)
    //空间 O(n*w)
    //状态转移公式  : s[i][j] = min(s[i-1][j-1],s[i-1][j],s[i][j-1]) + (a[i]==b[j] ? 0:1 );
    public static int lwsDP(char[] a, int alen, char[] b, int blen) {
        int[][] state = new int[alen][blen]; //[i][j]=编辑次数  i->a[i],j->b[j]
        //第一个字母
        state[0][0] = a[0] == b[0] ? 0 : 1;
        //第一列(b[0]和所有a的单个字母比较)
        for (int i = 1; i < alen; i++) {
            state[i][0] = state[i - 1][0] + (a[i] == b[0] ? 0 : 1);
        }
        //第一行(a[0]和所有b的单个字母比较)
        for (int j = 1; j < blen; j++) {
            state[0][j] = state[0][j - 1] + (a[0] == b[j] ? 0 : 1);
        }
        //其余
        for (int i = 1; i < alen; i++) {
            for (int j = 1; j < blen; j++) {
                int prvMin = state[i - 1][j - 1];
                prvMin = prvMin < state[i - 1][j] ? prvMin : state[i - 1][j];
                prvMin = prvMin < state[i][j - 1] ? prvMin : state[i][j - 1];
                state[i][j] = prvMin + (a[i] == b[j] ? 0 : 1);
            }
        }
        return state[alen - 1][blen - 1];
    }

    //动态规划,最长公共子串
    //时间 O(n*w)
    //空间 O(n*w)
    //1. a[i]==b[j] 时,最大公共子串数值+1,进入下一状态 i++ j++
    //2. a[i]!=b[j] 时,最大公共子串数值不变,进入下一状态
    // 2.1. i++ j不变 ,相当于a中当前第i个字b符被虚拟删掉(略过),或相当于在b中虚拟插入一个a[i]相同的字符(并不需要真的插入),然后考察下一字符:i++ j不变
    // 2.2. i不变 j++ , 相当于b中当前第j个字符被虚拟删掉(略过),或相当于在a中虚拟插入一个b[j]相同的字符(并不需要真的插入),然后考察下一字符:i不变 j++
    //
    //和上面莱文氏距离的代码非常相似,只改了10个字符
    //状态转移公式  : s[i][j] = max(s[i-1][j-1],s[i-1][j],s[i][j-1]) + (a[i]==b[j] ? 1:0 );
    public static int lcsDP(char[] a, int alen, char[] b, int blen) {
        int[][] state = new int[alen][blen]; //[i][j]=编辑次数  i->a[i],j->b[j]
        //第一个字母
        state[0][0] = a[0] == b[0] ? 1 : 0;
        //第一列(b[0]和所有a的单个字母比较)
        for (int i = 1; i < alen; i++) {
            state[i][0] = state[i - 1][0] + (a[i] == b[0] ? 1 : 0);
        }
        //第一行(a[0]和所有b的单个字母比较)
        for (int j = 1; j < blen; j++) {
            state[0][j] = state[0][j - 1] + (a[0] == b[j] ? 1 : 0);
        }
        //其余
        for (int i = 1; i < alen; i++) {
            for (int j = 1; j < blen; j++) {
                int prvMax = state[i - 1][j - 1];
                prvMax = prvMax > state[i - 1][j] ? prvMax : state[i - 1][j];
                prvMax = prvMax > state[i][j - 1] ? prvMax : state[i][j - 1];
                state[i][j] = prvMax + (a[i] == b[j] ? 1 : 0);
            }
        }
        return state[alen - 1][blen - 1];
    }

    //纠错,对输入单词a,找到词库中与之对应最接近的单词(莱文氏距离最小的),若莱文氏距离同样小,选最大公共子串值最大的
    public static char[] errorCorrection(char[] a, List<char[]> list) {
        int minlws = Integer.MAX_VALUE;
        int maxlcs = Integer.MIN_VALUE;
        int index = -1;
        for (int i = 0; i < list.size(); i++) {
            int tmplws = lwsDP(a, a.length, list.get(i), list.get(i).length);
            int tmplcs = lcsDP(a, a.length, list.get(i), list.get(i).length);
//            System.out.println("错误纠正 最小差异度:" + tmplws + " , 最大相同度:" + tmplcs + " - " + new String(a) + " - " + new String(list.get(i)));
            if (tmplws < minlws) {
                minlws = tmplws;
                index = i;
            }
            if(tmplcs > maxlcs) {
                maxlcs = tmplcs;
                index = i;
            }
        }
        if (index != -1) {
            System.out.println("错误纠正 最小差异度:" + minlws + " , 最大相同度:" + maxlcs);
            return list.get(index);
        }
        return "".toCharArray();
    }

    public static void main(String[] ar) {
        char[] a = "abcdef".toCharArray();
        char[] b = "abdxf".toCharArray();

        //1.莱文氏距离(差异度)
        System.out.println("===== 莱文氏距离(差异度) =====");
        int lws = lwsDP(a, a.length, b, b.length);
        System.out.println("莱文氏距离 lws :" + lws);

        //2.最大公共子串(相同度)
        System.out.println("\n===== 最大公共子串(相同度) =====");
        int lcs = lcsDP(a, a.length, b, b.length);
        System.out.println("最大公共子串 lcs :" + lcs);

        //3.单词错误纠正
        System.out.println("\n===== 单词错误纠正 =====");
        List<char[]> list = new LinkedList<>();
        list.add("hello".toCharArray());
        list.add("here".toCharArray());
        list.add("hex".toCharArray());
        list.add("her".toCharArray());
        list.add("hill".toCharArray());
        list.add("毛子".toCharArray());
        list.add("毛熊子".toCharArray());
        list.add("熊孩子".toCharArray());
        char[] input1 = "herro".toCharArray();
        System.out.println("输入 [" + new String(input1) + "] 被纠正为 [" + new String(errorCorrection(input1, list)) + "]");
        input1 = "h".toCharArray();
        System.out.println("输入 [" + new String(input1) + "] 被纠正为 [" + new String(errorCorrection(input1, list)) + "]");
        input1 = "子".toCharArray();
        System.out.println("输入 [" + new String(input1) + "] 被纠正为 [" + new String(errorCorrection(input1, list)) + "]");
        input1 = "熊".toCharArray();
        System.out.println("输入 [" + new String(input1) + "] 被纠正为 [" + new String(errorCorrection(input1, list)) + "]");
    }
}

输出

===== 莱文氏距离(差异度) =====
莱文氏距离 lws :2

===== 最大公共子串(相同度) =====
最大公共子串 lcs :4

===== 单词错误纠正 =====
错误纠正 最小差异度:1 , 最大相同度:4
输入 [herro] 被纠正为 [here]
错误纠正 最小差异度:2 , 最大相同度:1
输入 [h] 被纠正为 [hex]
错误纠正 最小差异度:1 , 最大相同度:1
输入 [子] 被纠正为 [毛子]
错误纠正 最小差异度:2 , 最大相同度:1
输入 [熊] 被纠正为 [毛熊子]

原文地址:https://www.cnblogs.com/cyy12/p/12020154.html

时间: 2024-10-06 11:07:41

单词错误纠正功能 编辑距离 最大公共字串 两个字符串的相似度 差异度的相关文章

最长递归子序列、最长公共字串、最长公共子序列、编辑距离

[TOC]   ### 最长递归子序列 #### 题目 给定数组arr,返回arr中的最长递增子序列,如`arr=[2,1,5,3,6,4,8,9,7]`,返回的最长递增子序列为`[1,3,4,8,9]` #### 题解思路 先用DP来求解子序列递增的最大长度,如arr的长度序列为`dp=[1,1,2,2,3,3,4,5,4]`,然后对这个长度序列dp从右到左遍历,得到最长递增子序列. 1. 求解长度序列,令dp[i]表示在以arr[i]这个数结尾的情况下,arr[0...i]中的最大递增子序列

搜索里的相似度计算-最长公共字串

相似度计算的任务是根据两段输入文本的相似度返回从0到1之间的相似度值:完全不相似,则返回0,:完全相同,返回1.衡量两端文字距离的常用方法有:海明距离(Hamming distance),编辑距离,欧氏距离,文档向量的夹角余弦距离,最长公共字串. 1. 余弦相似度 把两篇文档看作是词的向量,如果x,y为两篇文档的向量,则:Cos(x, y) = (x * y) / (||x|| * ||y||) 使用Hashmap可以很方便的把这个计算出来 2. 最长公共字串(Longest Common Su

公共字串计算——String.regionMatches方法 &amp; Java标签

题目:输入两个字符串,计算两个字符串的最大公共字串的长度,并输出,字符不区分大小写 eg:输入abcde  xxxBcyyy,输出 2. 完整Java代码: import java.util.*; public class Main { public static void main(String arg[]){ Scanner s=new Scanner(System.in); String str1=s.next(); String str2=s.next(); s.close(); Str

华为OJ: 公共字串计算

有几个需要注意的地方,一个这道题是不区分大小写的,所以在计算之前对输入的字符串要做小写或者大写的转换. 第二个,思路一定要清晰,先将s1从[i]处开始与s2的[j]开始匹配,不相等则j++直到j等于s2.length()-1,相等,则i++,j++.注意,这里就是i++,即下次重新开始从s[i]开始匹配时,两次i之间的距离可能会超过1.再j那里设置一个计数器计数即可. import java.util.Scanner; public class findMaxSubStringLength {

URAL 1517 Freedom of Choice(后缀数组,最长公共字串)

题目 输出最长公共字串 #define maxn 200010 int wa[maxn],wb[maxn],wv[maxn],ws[maxn]; int cmp(int *r,int a,int b,int l) {return r[a]==r[b]&&r[a+l]==r[b+l];}//yuan lai zhi qian ba zhe li de l cuo dang cheng 1 le ... void da(int *r,int *sa,int n,int m) { int i,j

(字符串)最长公共字串(Longest-Common-SubString,LCS)

题目: 给定两个字符串X,Y,求二者最长的公共子串,例如X=[aaaba],Y=[abaa].二者的最长公共子串为[aba],长度为3. 子序列是不要求连续的,字串必须是连续的. 思路: 1.简单思想: 遍历两个字符串X.Y,分别比较X的字串与Y的字串,求出最长的公共字串. #include <iostream> #include <vector> using namespace std; int getComLen(char *str1,char *str2){ int len=

华为机试-公共字串计算

题目描述题目标题:计算两个字符串的最大公共字串的长度,字符不区分大小写详细描述:接口说明原型:int getCommonStrLength(char * pFirstStr, char * pSecondStr);输入参数: char * pFirstStr //第一个字符串 char * pSecondStr//第二个字符串 输入描述:输入两个字符串 输出描述:输出一个整数 输入例子:asdfas werasdfaswer 输出例子:6 效率低的Java程序实现: import java.ut

【编程题目】最长公共字串

56.最长公共字串(算法.字符串).题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串.注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中.请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串.例如:输入两个字符串 BDCABA 和 ABCBDAB,字符串 BCBA 和 BDAB 都是是它们的最长公共子串,则输出它们的长度 4,并打印任意一个子串. 经典动态规划题. #include <stdio.h> #i

华为OJ——公共字串计算

题目描述 题目标题: 计算两个字符串的最大公共字串的长度,字符不区分大小写 详细描述: 接口说明 原型: int getCommonStrLength(char * pFirstStr, char * pSecondStr); 输入参数: char * pFirstStr //第一个字符串 char * pSecondStr//第二个字符串 输入描述: 输入两个字符串 输出描述: 输出一个整数 输入例子: asdfas werasdfaswer 输出例子: 6 import java.util.