不同長度的字串/中文串相似度對比演算法
1.背景介紹
今天在公司接到一個需求,大概是這樣的.我們ERP系統資料庫有張customer(客戶)表,其中有個欄位是小區名稱(plotName),當初在錄入資料時沒有對這一欄位做界定和規範,由人工手動錄入,這就導致兩位客戶本是一個小區,而錄入的小區名可能不是完全一樣的結果.例如張三和李四都住在武林邸,而張三錄入的資料是"武林邸",李四錄入的資料的"杭州市西湖區武林邸",又或是舞林邸等.由於業務需要,現需要對這些小區名進行名稱規整,如上述例子都規整為"杭州市西湖區武林邸".
2.解決方案如下
既然要規整,就要有統一的名稱規範,最終決定從安居客,租房網等爬出杭州市各小區的標準名稱.再用資料庫現有資料與之對比.列出最相似的前三個名稱,然後澤其一修資料庫資料.
如下圖所示:匹配度及為相似度演算法計算所得結果.
3.難點
難點就是字串相似度對比演算法的設計,網上查閱大量資料,如字串最小編輯距離匹配演算法此處不適用,因為對比的字串與目標字串長度不一樣,會導致對比結果不是正確的.用程式碼和資料驗證如下:
對比的三個字串如下: 目標字串為:"龍悅灣金石院子"
- "浙江省杭州市拱墅區龍悅灣金石院子"
- "金石院子十六幢二單元302"
- "金子"
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;
}
執行結果:正確