網站首頁 編程語言 正文
DFA 算法是通過提前構造出一個 樹狀查找結構,之后根據輸入在該樹狀結構中就可以進行非常高效的查找。
設我們有一個敏感詞庫,詞酷中的詞匯為:
- 我愛你
- 我愛他
- 我愛她
- 我愛你呀
- 我愛他呀
- 我愛她呀
- 我愛她啊
那么就可以構造出這樣的樹狀結構:
設玩家輸入的字符串為:白菊我愛你呀哈哈哈
我們遍歷玩家輸入的字符串 str,并設指針 i 指向樹狀結構的根節點,即最左邊的空白節點:
- str[0] = ‘白’ 時,此時 tree[i] 沒有指向值為 ‘白’ 的節點,所以不滿足匹配條件,繼續往下遍歷
- str[1] = ‘菊’,同樣不滿足匹配條件,繼續遍歷
- str[2] = ‘我’,此時 tree[i] 有一條路徑連接著 ‘我’ 這個節點,滿足匹配條件,i 指向 ‘我’ 這個節點,然后繼續遍歷
- str[3] = ‘愛’,此時 tree[i] 有一條路徑連著 ‘愛’ 這個節點,滿足匹配條件,i 指向 ‘愛’,繼續遍歷
- str[4] = ‘你’,同樣有路徑,i 指向 ‘你’,繼續遍歷
- str[5] = ‘呀’,同樣有路徑,i 指向 ‘呀’
此時,我們的指針 i 已經指向了樹狀結構的末尾,即此時已經完成了一次敏感詞判斷。我們可以用變量來記錄下這次敏感詞匹配開始時玩家輸入字符串的下標,和匹配結束時的下標,然后再遍歷一次將字符替換為 * 即可。
結束一次匹配后,我們把指針 i 重新指向樹狀結構的根節點處。
此時我們玩家輸入的字符串還沒有遍歷到頭,所以繼續遍歷:
str[6] = ‘哈’,不滿足匹配條件,繼續遍歷
str[7] = ‘哈’ …
str[8] = ‘哈’ …
可以看出我們遍歷了一次玩家輸入的字符串,就找到了其中的敏感詞匯。
DFA算法python實現
class DFA:
"""DFA 算法
敏感字中“*”代表任意一個字符
"""
def __init__(self, sensitive_words: list, skip_words: list): # 對于敏感詞sensitive_words及無意義的詞skip_words可以通過數據庫、文件或者其他存儲介質進行保存
self.state_event_dict = self._generate_state_event(sensitive_words)
self.skip_words = skip_words
def __repr__(self):
return '{}'.format(self.state_event_dict)
@staticmethod
def _generate_state_event(sensitive_words) -> dict:
state_event_dict = {}
for word in sensitive_words:
tmp_dict = state_event_dict
length = len(word)
for index, char in enumerate(word):
if char not in tmp_dict:
next_dict = {'is_end': False}
tmp_dict[char] = next_dict
tmp_dict = next_dict
else:
next_dict = tmp_dict[char]
tmp_dict = next_dict
if index == length - 1:
tmp_dict['is_end'] = True
return state_event_dict
def match(self, content: str):
match_list = []
state_list = []
temp_match_list = []
for char_pos, char in enumerate(content):
if char in self.skip_words:
continue
if char in self.state_event_dict:
state_list.append(self.state_event_dict)
temp_match_list.append({
"start": char_pos,
"match": ""
})
for index, state in enumerate(state_list):
is_match = False
state_char = None
if '*' in state: # 對于一些敏感詞,比如大傻X,可能是大傻B,大傻×,大傻...,采用通配符*,一個*代表一個字符
state_list[index] = state['*']
state_char = state['*']
is_match = True
if char in state:
state_list[index] = state[char]
state_char = state[char]
is_match = True
if is_match:
if state_char["is_end"]:
stop = char_pos + 1
temp_match_list[index]['match'] = content[
temp_match_list[index]['start']:stop]
match_list.append(copy.deepcopy(temp_match_list[index]))
if len(state_char.keys()) == 1:
state_list.pop(index)
temp_match_list.pop(index)
else:
state_list.pop(index)
temp_match_list.pop(index)
for index, match_words in enumerate(match_list):
print(match_words['start'])
return match_list
_generate_state_event方法生成敏感詞的樹狀結構,(以字典保存),對于上面的例子,生成的樹狀結構保存如下:
if __name__ == '__main__':
dfa = DFA(['我愛你', '我愛他', '我愛她', '我愛你呀', '我愛他呀', '我愛她呀', '我愛她啊'], skip_words=[]) # 暫時不配置skip_words
print(dfa)
結果:
{'我': {'is_end': False, '愛': {'is_end': False, '你': {'is_end': True, '呀': {'is_end': True}}, '他': {'is_end': True, '呀': {'is_end': True}}, '她': {'is_end': True, '呀': {'is_end': True}, '啊': {'is_end': True}}}}}
然后調用match方法,輸入內容進行敏感詞匹配:
if __name__ == '__main__':
dfa = DFA(['我愛你', '我愛他', '我愛她', '我愛你呀', '我愛他呀', '我愛她呀', '我愛她啊'], ['\n', '\r\n', '\r'])
# print(dfa)
print(dfa.match('白菊我愛你呀哈哈哈'))
結果:
[{'start': 2, 'match': '我愛你'}, {'start': 2, 'match': '我愛你呀'}]
而對于一些敏感詞,比如大傻X,可能是大傻B,大傻×,大傻...,那是不是可以通過一個通配符*來解決?
見代碼:48 ~51行
if '*' in state: # 對于一些敏感詞,比如大傻X,可能是大傻B,大傻×,大傻...,采用通配符*,一個*代表一個字符
state_list[index] = state['*']
state_char = state['*']
is_match = True
驗證一下:
if __name__ == '__main__':
dfa = DFA(['大傻*'], [])
print(dfa)
print(dfa.match('大傻X安樂飛大傻B'))
{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[{'start': 0, 'match': '大傻X'}, {'start': 6, 'match': '大傻B'}]
上列中如果輸入的內容中,“大傻X安樂飛大傻B”寫成“大%傻X安樂飛大&傻B”,看看是否能識別出敏感詞呢?識別不出了!
if __name__ == '__main__':
dfa = DFA(['大傻*'], [])
print(dfa)
print(dfa.match('大%傻X安樂飛大&傻B'))
結果:
{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[
諸如“,&,!,!,@,#,$,¥,*,^,%,?,?,<,>,《,》",這些特殊符號無實際意義,但是可以在敏感詞中間插入而破壞敏感詞的結構規避敏感詞檢查
進行無意義詞配置,再進行敏感詞檢查,如下,可見對于被破壞的敏感詞也能識別
if __name__ == '__main__':
dfa = DFA(['大傻*'], ['%', '&'])
print(dfa)
print(dfa.match('大%傻X安樂飛大&傻B'))
結果:?
{'大': {'is_end': False, '傻': {'is_end': False, '*': {'is_end': True}}}}
[{'start': 0, 'match': '大%傻X'}, {'start': 7, 'match': '大&傻B'}]
原文鏈接:https://www.cnblogs.com/fdzwdt/p/16174752.html
相關推薦
- 2022-08-18 Kotlin函數使用示例教程_Android
- 2023-06-18 C#中關于double.ToString()的用法_C#教程
- 2023-12-16 SpringBoot 配置文件使用@ @取值
- 2024-03-15 npm install報錯 Fix the upstream dependency conflict
- 2022-08-11 python中的多線程鎖lock=threading.Lock()使用方式_python
- 2022-05-22 Python學習之os包使用教程詳解_python
- 2024-03-10 @Controller、@Service和@Repository注解詳解
- 2022-09-07 基于域名的方式訪問Istio服務網格中的多個應用程序的方法詳解_相關技巧
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支