專案中模糊匹配演算法的實現

2022-06-12 15:33:13 字數 4001 閱讀 8783

最近在專案中遇到了很多模糊匹配字串的需求,總結一下實現思路。

大體需求場景是這樣的:省專案中,各個地市報送的本地證照目錄非常不規範,只有不規範的證照名稱,且沒有與國家標準證照目錄中的證照名稱進行對應。需要將這些名稱不規範的證照與國家標準目錄中的證照對應起來。

拿到乙個不規範的證照名稱,需要將其與國家標準目錄中的證照名稱進行一一比對,並選取匹配度最高的乙個國家標準證照作為結果。

匹配度的計算

那麼首先,需要設計乙個方法,對兩個字串進行模糊匹配並計算兩個字串的匹配度。

字串比對,首先想到了 kmp 演算法。但原生的 kmp 演算法只能用來判斷兩個字串的包含關係,「匹配度」 並不是只用是否包含就可以表示的。比如 「建設工程施工許可」、「施工(房屋)許可證書」雖然不存在包含關係,但實際上是同乙個證照。「匹配度」 應該由兩個字串的最長公共子串行來描述更為確切。

乙個字串的 子串行 是指這樣乙個新的字串:它是由原字串在不改變字元的相對順序的情況下刪除某些字元(也可以不刪除任何字元)後組成的新字串。例如,"ace" 是 "abcde" 的子串行,但 "aec" 不是 "abcde" 的子串行。兩個字串的「公共子串行」是這兩個字串所共同擁有的子串行。 

使用 最長公共子串行的長度%較短字串的長度 可以很好的描述兩個字串的匹配度。

求取最長公共子串行的暴力實現:

public

intlongestcommonsubsequence(string text1, string text2)

/*** @author niuxy

* @date 2020/10/2 7:12 下午

* @description

* 最長公共子串行實現中,每個節點將面臨四種不同的選擇情況,設 n=max(str1.length,str2.length),時間複雜度為 3 的 n 次冪

* 函式 searchsame( point1,point2 ) 表示 在 point1,point2 指向的位元組前的字串最大的重合長度

* 則可以發現,遞迴過程中,每對 point 指標指向的結果是可以被復用的

* 建立快取避免重複計算

*/private static int searchsame(string str1, string str2, int point1, int point2, int

cache)

遞迴優化為遞推,在快取表上進行二維 dp :

public static 

intlongestcommonsubsequencedp(string str1, string str2)

}return cache[0][0];

}

時間複雜度又 3 的 max(n,m) 次冪 優化為 n*m (n,m 分別為兩個入參字串的長度)。至此,求取最長公共子串行的方法便確定下來了。

因為快取使用的是二維陣列,需要連續的儲存空間,在待比較字串長度較長時所需連續空間較大。空間較為緊張時可使用 map 來替代陣列:

public

static

intlongestcommonsubsequencedp(string str1, string str2)

}return cache.get(getkey(0, 0));

}private

static

long getkey(int i, int

j)

在 key 值的計算上使用了 i,j 分別表示 long 高低位位元組的方式來避免 key 的衝突。

最長公共子串行的長度 % min(n,m) 便可表示重合率:

//

計算重合率

private

static

double coincidencerate(string str1, string str2, int

length)

去除冗餘資訊在計算匹配度前,應當去除字串中的冗餘資訊,進一步提高比對的精度。

比如 「中華人民共和國結婚證」、「中國結婚證書」 中,「中華人民共和國」 與 「中國」 對於比對來說,實際上是冗餘資訊。比對這兩個字串是否是同乙個證照,只需要比對 「結婚證」 三個字即可。一股腦的對所有資訊進行比對,會造成較大的誤差。

因此在比對前,對於一些可以提前確定的常用的冗餘資訊,應當提前去除掉。比如對於證照名稱比對的場景來說:「中華人民共和國」、「中國」、「山東省」、「xx市」、"書" 等都是應當提前去除的冗餘資訊。「中華人民共和國結婚證」 與 「山東省結婚證」 實際上都是同乙個證照。

private

static

string deleteredundances(string str, string redundances)

return

stringbuilder.tostring();

}

可以將可預判的冗餘資訊放在 redundances 中,將 str 中的冗餘資訊依次剔除。

閾值的設定

可以計算兩個字串的重合率,並可以提前去除一些冗餘欄位來提公升判斷的精度後,還需要對閾值的設定進行支援。

重合率高於閾值時,則認定兩個字串為相似字串,指向同一證照。

//

比較重合率與閾值

public

static

boolean issame(string str1, string str2, int length, double

threshold)

至此,乙個通用且簡陋的模糊查詢工具便完成了,完整**:

/**

* @author niuxy

* @date 2020/9/30 1:12 下午

* @description 字串模糊匹配 */

public

class

stringcompareutil

//比較重合率與閾值

public

static

boolean issame(string str1, string str2, int length, double

threshold)

//計算重合率

private

static

double coincidencerate(string str1, string str2, int

length)

//去處冗餘

private

static

string deleteredundances(string str, string redundances)

return

stringbuilder.tostring();

}//計算最長公共子串行

public

static

intlongestcommonsubsequence(string str1, string str2)

}return cache[0][0];}}

針對特定場景(證照名稱比對)的使用:

public

class

licensestringutils ;

static

public

boolean

issame(string str0, string str1)

}

方法簡單,但可以滿足我目前的需求。

只是對於一些及其相似的證照,比如 「中華人民共和國殘疾人證」、「中華人民共和國殘疾軍人證」,在閾值為 0.75 時無法分辨。

閾值設定太大又會造成一些證照的漏配,像這種極為相似的證照,就需要在閾值的選擇上和冗餘資訊的選擇上進行更貼合使用場景的優化了。

因為我的需求場景要求沒有這麼細緻,且該類情況較少。類似的情況我直接在後期進行了人工干預(按名稱排序人工檢查一下即可)、

歡迎指出更好的方案或優化建議。

php 模糊匹配 PHP模糊查詢的實現方法 推薦

模式查詢 1.sql匹配模式 2.正規表示式匹配模式 一般不推薦使用 sql匹配模式 1.使用sql匹配模式,不能使用操作符 或!而是使用操作符like或not like 2.使用sql匹配模式,mysql提供了2種萬用字元。表示任意數量的任意字元 其中包括0個 表示任意單個字元 3.使用sql匹配...

Django專案中的ORM對映與模糊查詢

一喪 我也不想墮落 可我沒有快樂 orm對映 什麼是orm對映?在筆者認為就是對sql語句的封裝,所寫語句與sql對應語句含義相同,使開發更加簡單方便,不過也是存在弊端的,使程式執行效率下降。例如 id 2 等於 select from user userinfo where id 2 修改管理器 ...

資料庫禁止模糊匹配的情況下實現模糊匹配以及分頁展示

部分示例 如下 自己重寫的偽 非專案原 獲取資料庫所有rb title資料 title x getinstance getlist rb id gt 0 rb title 拼接自動下拉框外掛程式要求字串 title all array column title,rb title foreach ti...