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

Posted jald

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单词错误纠正功能 编辑距离 最大公共字串 两个字符串的相似度 差异度相关的知识,希望对你有一定的参考价值。

 

莱文氏距离

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
输入 [熊] 被纠正为 [毛熊子]

 

以上是关于单词错误纠正功能 编辑距离 最大公共字串 两个字符串的相似度 差异度的主要内容,如果未能解决你的问题,请参考以下文章

单词拼写检查之cutoff距离

51nod 1183 编辑距离线性dp+类似最长公共子序列

Levenshtein distance 编辑距离

最长递增子序列 && 最大子序列最长递增子序列最长公共子串最长公共子序列字符串编辑距离

华为机试-公共字串计算

【python】求两个字符串的公共字串?