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

莱文氏距离

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

猜你喜欢

转载自www.cnblogs.com/cyy12/p/12020154.html