1. 程式人生 > >不同長度的字串/中文串相似度對比演算法

不同長度的字串/中文串相似度對比演算法

1.背景介紹

今天在公司接到一個需求,大概是這樣的.我們ERP系統資料庫有張customer(客戶)表,其中有個欄位是小區名稱(plotName),當初在錄入資料時沒有對這一欄位做界定和規範,由人工手動錄入,這就導致兩位客戶本是一個小區,而錄入的小區名可能不是完全一樣的結果.例如張三和李四都住在武林邸,而張三錄入的資料是"武林邸",李四錄入的資料的"杭州市西湖區武林邸",又或是舞林邸等.由於業務需要,現需要對這些小區名進行名稱規整,如上述例子都規整為"杭州市西湖區武林邸".

2.解決方案如下

既然要規整,就要有統一的名稱規範,最終決定從安居客,租房網等爬出杭州市各小區的標準名稱.再用資料庫現有資料與之對比.列出最相似的前三個名稱,然後澤其一修資料庫資料.

如下圖所示:匹配度及為相似度演算法計算所得結果.

3.難點

難點就是字串相似度對比演算法的設計,網上查閱大量資料,如字串最小編輯距離匹配演算法此處不適用,因為對比的字串與目標字串長度不一樣,會導致對比結果不是正確的.用程式碼和資料驗證如下:

對比的三個字串如下:                                     目標字串為:"龍悅灣金石院子"

  1. "浙江省杭州市拱墅區龍悅灣金石院子"
  2. "金石院子十六幢二單元302"
  3. "金子"
 public static void main(String[] args){
        LinkedList list=new LinkedList();
        String source = "龍悅灣金石院子";//目標字串
        String target1 = "浙江省杭州市拱墅區龍悅灣金石院子";
        String target2="金石院子十六幢二單元302";
        String target3="金子";
        System.out.println(target1+"最小編輯距離是:"+minEdit_distance(source,target1));
        System.out.println(target2+"最小編輯距離是:"+minEdit_distance(source,target2));
        System.out.println(target3+"最小編輯距離是:"+minEdit_distance(source,target3));
    }

其中minEdit_distance()方法是最小編輯距離演算法,根據此演算法的思想,如果最小編輯距離值越小,則說明兩個字串越相似.而此處列印結果為9,12,5.即"金子"與"龍悅灣金石院子"最相似,"浙江省杭州市拱墅區龍悅灣金石院子"第二,"金石院子十六幢二單元302"第三,而實際應該是"金子"與"龍悅灣金石院子"最不相似,是不期望得到的結果.

列印結果:

4.最終解決方案

將字串和目標字串去空格後轉為單字符集,定義一個變數intersection,自定義演算法計算讓intersection值增加,相似度百分比值=intersection*2/unionLength(兩個字串長度和)*100.

程式碼如下:

 public static String compareStrings(String str1, String str2) {
        DecimalFormat df = new DecimalFormat("0.00");//格式化小數
        ArrayList pairs1 = wordLetterPairs(str1.toUpperCase());
        ArrayList pairs2 = wordLetterPairs(str2.toUpperCase());
        int intersection = 0;
        int union = pairs1.size() + pairs2.size();
        for (int i=0; i<pairs1.size(); i++) {
            Object pair1=pairs1.get(i);
            for(int j=0; j<pairs2.size(); j++) {
                Object pair2=pairs2.get(j);
                if (pair1.equals(pair2)) {
                    intersection++;
                    pairs2.remove(j);
                    break;
                }
            }
        }
        return df.format((2.0*intersection)/union*100)+"%";
    }
private static String[] letterPairs(String str) {
        int numPairs = str.length()-1;
        String[] pairs = new String[numPairs];
        for (int i=0; i<numPairs; i++) {
            pairs[i] = str.substring(i,i+2);
        }
        return pairs;
    }

    private static ArrayList wordLetterPairs(String str) {
        ArrayList allPairs = new ArrayList();
        String[] words = str.split("\\s");
        for (int w=0; w < words.length; w++) {
            String[] pairsInWord = letterPairs(words[w]);
            for (int p=0; p < pairsInWord.length; p++) {
                allPairs.add(pairsInWord[p]);
            }
        }
        return allPairs;
    }

執行結果:正確