zoukankan      html  css  js  c++  java
  • 单词错误纠正功能 编辑距离 最大公共字串 两个字符串的相似度 差异度

    莱文氏距离

    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("
    ===== 最大公共子串(相同度) =====");
            int lcs = lcsDP(a, a.length, b, b.length);
            System.out.println("最大公共子串 lcs :" + lcs);
    
            //3.单词错误纠正
            System.out.println("
    ===== 单词错误纠正 =====");
            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
    输入 [熊] 被纠正为 [毛熊子]

  • 相关阅读:
    软考过后
    最近
    软考复习初体验
    再看提高班
    C语言深入学习系列 字节对齐&内存管理
    C++ 阶段一(已完成)
    小弟,开博学习了!!
    [学习心得] 我总结的进制转换
    《深入浅出设计模式》一书学习(.net版—简单工厂)
    安装mysql 获得 mysql.h 建立C接口
  • 原文地址:https://www.cnblogs.com/cyy12/p/12020154.html
Copyright © 2011-2022 走看看