通過ahocorasick快速構建一棵actree(AC自動機)

參考自文章:https://www.cnblogs.com/always-fight/p/10895984.html

1. 作用

本質上說就是字符串匹配,比如根據一句話我們可以從字典樹中快速匹配出字符串交集(快速指的是隻遍歷一遍字典樹,比KMP算法要快),通常應用在問答系統中用於提取語句的關鍵詞。

2. 例子

比如有一個wordlist列表,包含43430個元素:

['長春海外製藥接骨續筋片', '香菇燉甲魚', '三鶴藥業黃柏膠囊', '上海衡山熊去氧膽酸片', '升和藥業依託泊苷注射液', '怡諾思', '人格障礙', '轉鐵蛋白飽和度', '脾囊腫', '素燒白蘿蔔', '利君現代冠脈寧片', 
'上海復華藥業注射用還原型谷', '陰囊上有白色小疙瘩', '腹痛伴休克', '成都通德胰激肽原酶腸溶片', '蒸豬肝', '河北百善血尿膠囊', '精神障礙', '輸卵管畸形', '元和抑眩寧膠囊', '蓮藕豆腐', '辰欣哈西奈德溶液', 
'信誼煙酸片', '慢性膽囊炎', '參芪降糖顆粒', '康普藥業鹽酸普萘洛爾片', '西安迪賽胸腺肽腸溶片', '雙鷺藥業注射用複合輔酶', '慢性篩竇炎', '新高製藥維胺酯維E乳膏', '冰黃膚樂軟膏', '神經類疾病', '液晶熱圖', 
'棗(幹)', '股外側皮神經病', '浙江惠松硅炭銀片', '牙根外露', '湖北潛江氯黴素滴眼液', '鹽類皮質激素分泌過多', '五子衍宗丸', '小兒陣發性睡眠性血紅蛋白尿症', '功能失調性子宮出血病', '茵梔黃口服液', 
'眼底出血和滲出', '斯達製藥注射用頭孢噻肟鈉', '複方白芷酊', '脛腓骨骨折', '西南藥業氯黴素片', '宮頸炎', '茶鹼緩釋膠囊', '原發性硬化性膽管炎', '鄭州韓都利肺膠囊', '咽反射消失', '脊髓灰質炎', 
'甲狀腺片', '迴盲瓣功能不全', '乙肝e抗體(抗...', '馬齒莧粥', '動脈硬化', '寶寶樂', '腸閉鎖', '肺放線菌病', '江蘇晨牌產婦安顆粒', '犬吠樣咳嗽', '胃康靈膠囊', '小兒煙酸缺乏病', '青龍防風通聖丸', 
'廣東南國維生素C片', '碘化油咀嚼片', '西樂葆', '偉哥甲磺酸酚妥拉明分散片', '成都迪康藥業樟腦醑', '斑疹', '五花燉墨魚', '肉燉芸豆粉條', '陝西東泰製藥益脈康膠囊', '桔梗八味顆粒', '華南牌溴丙胺太林片', 
'吉林敖東洮南小牛脾提取物注', '仁青芒覺', '血吸蟲病與肝膽疾病',...,'持續性枕橫位難產', '彎曲菌感染', '絲瓜蘑菇肉片湯', '長春銀諾克清咽片', '肝葉萎縮', '迪皿鹽酸左西替利嗪口服溶液']

轉換成字典樹後的結構是 index,(index,word)

0 (0, '長春海外製藥接骨續筋片')
1 (1, '香菇燉甲魚')
2 (2, '三鶴藥業黃柏膠囊')
3 (3, '上海衡山熊去氧膽酸片')
4 (4, '升和藥業依託泊苷注射液')
5 (5, '怡諾思')
6 (6, '人格障礙')
7 (7, '轉鐵蛋白飽和度')
8 (8, '脾囊腫')
9 (9, '素燒白蘿蔔')
10 (10, '利君現代冠脈寧片')
......
43422 (43422, '彎曲菌感染')
43423 (43423, '絲瓜蘑菇肉片湯')
43424 (43424, '長春銀諾克清咽片')
43425 (43425, '肝葉萎縮')
43426 (43426, '迪皿鹽酸左西替利嗪口服溶液')
43427 (43427, '華潤天和麝香壯骨膏')
43428 (43428, '湖北恆安曲咪新乳膏')
43429 (43429, '子宮小')

建樹的過程如下:

import ahocorasick
actree=ahocorasick.Automaton()
for index,word in enumetate(wordlist):
    actree.add_word(word,(index,word))
actree.make_automaton()

快速匹配:

for i in actree.iter('昨天發燒,服用了阿司匹林,並且還吃了牛黃清胃丸,飯是吃了瓜燒白菜,
                                                                    大便有點色淺')
    print(i)

這樣客戶輸入一個字符串,我們能夠快速的從之前的列表中匹配出相應的實體元素:

print(wordlist[11188])
print(wordlist[41305])
print(wordlist[43198])
print(wordlist[18263])

果然我們通過查看索引,與上圖結果一致:

思考:

存在一個問題,從上面的客戶輸入看,客戶輸入了瓜燒白菜,但是匹配出了白菜和瓜燒白菜,我們從客戶輸入看,客戶是想輸入瓜燒白菜,白菜我們並不想匹配出來

region_wds = []
for i in actree.iter('昨天發燒,服用了阿司匹林,並且還吃了牛黃清胃丸,飯是吃了瓜燒白菜,大便有點色淺'):
    wd = i[1][1]
    region_wds.append(wd)
 
stop_wds = []
for wd1 in region_wds:
    for wd2 in region_wds:
        if wd1 in wd2 and wd1 != wd2:
            print(wd1, wd2)
            stop_wds.append(wd1)
             
final_wds = [ i for i in region_wds if i not in stop_wds ]
     
print(region_wds)
print(stop_wds)
print(final_wds)

上面作者的這個思路就是在所有匹配出的字符串中,如果有短的詞語存在長度詞語中,那麼去掉短的詞語,這也算是一個trick吧。

我們過濾掉了白菜,雖然白菜存在於列表元素中,顯然它不是客戶想表達的。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章