基于规则的分词
基于规则或词典的分词方法是一种较为机械的分词方法,其基本思想如下。
- 将待分词语句中的字符串和词典逐个匹配。
- 找到匹配的字符串则切分,不匹配则减去边缘的某些字符。
- 从头再次匹配,直至匹配完毕或者没有找到词典的字符串而结束。
基于规则分词主要方法如下。
- 正向最大匹配法(Maximum Match Method,MM法)。
- 逆向最大匹配法(Reverse Maximum Match Method,RMM法)。
- 双向最大匹配法(Bi-direction Matching Method,BMM法)。
def precompute_max_length(dictionary):
"""
计算词典中最大长度词的长度
:param dictionary: 词典
:return: 词典中最大长度词的长度
"""
return max(len(word) for word in dictionary) if dictionary else 0
def forward_segmentation(text, dictionary):
"""
正向最大匹配
:param text: 文本字符串
:param dictionary: 词典
:return: 将文本字符串中切词后的数组
"""
result = []
i = 0
max_len = precompute_max_length(dictionary)
while i < len(text):
found_word = ''
# 尝试最长可能的词长
max_possible = min(max_len, len(text) - i)
for l in range(max_possible, 0, -1):
candidate = text[i:i + l]
if candidate in dictionary:
found_word = candidate
break
if found_word:
result.append(found_word)
i += len(found_word)
else:
# 没有找到任何词,添加单个字符作为词汇
result.append(text[i])
i += 1
return result
def backward_segmentation(text, dictionary):
"""
逆向最大匹配
:param text: 文本字符串
:param dictionary: 词典
:return: 将文本字符串中切词后的数组
"""
result = []
i = len(text)
max_len = precompute_max_length(dictionary)
while i > 0:
found_word = ''
# 尝试最长可能的词长,从i处往回找
max_possible = min(max_len, i)
for l in range(max_possible, 0, -1):
candidate = text[i - l:i]
if candidate in dictionary:
found_word = candidate
break
if found_word:
result.insert(0, found_word)
i -= len(found_word)
else:
# 没有找到任何词,添加最后一个字符作为词汇
result.insert(0, text[i - 1])
i -= 1
return result
def bidirectional_segmentation(text, dictionary):
"""
双向最大匹配
:param text: 文本字符串
:param dictionary: 词典
:return: 将文本字符串中切词后的数组
"""
result = []
i = 0
n = len(text)
max_len = precompute_max_length(dictionary)
while i < n:
# 正向扫描查找词
forward_word = ''
max_possible_forward = min(max_len, n - i)
for l in range(max_possible_forward, 0, -1):
candidate = text[i:i + l]
if candidate in dictionary:
forward_word = candidate
break
# 反向扫描查找词
backward_word = ''
max_possible_backward = min(max_len, i)
for l in range(max_possible_backward, 0, -1):
candidate = text[i - l:i]
if candidate in dictionary:
backward_word = candidate
break
# 判断选择哪个词,如果长度相同则优先正向词?
if len(forward_word) >= len(backward_word):
result.append(forward_word)
i += len(forward_word)
else:
result.append(backward_word)
i -= len(backward_word)
return result
# 定义词典
word_set = {"中", "华", "民", "共和", "国", "人民", "共和国"}
# 定义文本字符串
text = "中华人民共和国"
# 正向分词
forward_result = forward_segmentation(text, word_set)
print("正向分词结果:", forward_result)
# 逆向分词
backward_result = backward_segmentation(text, word_set)
print("逆向分词结果:", backward_result)
# 双向分词
bidirectional_result = bidirectional_segmentation(text, word_set)
print("双向分词结果:", bidirectional_result)
正向分词结果: ['中', '华', '人民', '共和国']
逆向分词结果: ['中', '华', '人民', '共和国']
双向分词结果: ['中', '华', '人民', '共和国']
以下是代码的详细注释,解释了每个函数的功能和实现逻辑:
precompute_max_length(dictionary)
计算词典中最长单词的长度。
如果词典为空,则返回0。
def precompute_max_length(dictionary):
# 如果词典不为空,返回词典中最长单词的长度
return max(len(word) for word in dictionary) if dictionary else 0
forward_segmentation(text, dictionary)
正向最大匹配分词算法。
从左到右扫描文本,每次尝试匹配最长的单词。
def forward_segmentation(text, dictionary):
result = [] # 存储分词结果
i = 0 # 当前扫描位置
max_len = precompute_max_length(dictionary) # 获取词典中最长单词的长度
while i < len(text): # 遍历文本
found_word = '' # 用于存储找到的单词
# 计算当前扫描位置的最大可能匹配长度
max_possible = min(max_len, len(text) - i)
for l in range(max_possible, 0, -1): # 从最大可能长度开始递减
candidate = text[i:i+l] # 提取当前候选单词
if candidate in dictionary: # 如果候选单词在词典中
found_word = candidate # 找到匹配单词
break
if found_word: # 如果找到匹配单词
result.append(found_word) # 将单词加入结果
i += len(found_word) # 更新扫描位置
else: # 如果没有找到匹配单词
result.append(text[i]) # 将当前字符作为单字加入结果
i += 1 # 更新扫描位置
return result # 返回分词结果
backward_segmentation(text, dictionary)
逆向最大匹配分词算法。
从右到左扫描文本,每次尝试匹配最长的单词。
def backward_segmentation(text, dictionary):
result = [] # 存储分词结果
i = len(text) # 当前扫描位置(从文本末尾开始)
max_len = precompute_max_length(dictionary) # 获取词典中最长单词的长度
while i > 0: # 遍历文本
found_word = '' # 用于存储找到的单词
# 计算当前扫描位置的最大可能匹配长度
max_possible = min(max_len, i)
for l in range(max_possible, 0, -1): # 从最大可能长度开始递减
candidate = text[i-l:i] # 提取当前候选单词
if candidate in dictionary: # 如果候选单词在词典中
found_word = candidate # 找到匹配单词
break
if found_word: # 如果找到匹配单词
result.insert(0, found_word) # 将单词插入结果列表的开头
i -= len(found_word) # 更新扫描位置
else: # 如果没有找到匹配单词
result.insert(0, text[i-1]) # 将当前字符作为单字插入结果列表的开头
i -= 1 # 更新扫描位置
return result # 返回分词结果
bidirectional_segmentation(text, dictionary)
双向最大匹配分词算法。
结合正向和逆向最大匹配算法,选择匹配长度更长的单词。
def bidirectional_segmentation(text, dictionary):
result = [] # 存储分词结果
i = 0 # 当前扫描位置
n = len(text) # 文本长度
max_len = precompute_max_length(dictionary) # 获取词典中最长单词的长度
while i < n: # 遍历文本
# 正向扫描查找词
forward_word = '' # 存储正向匹配的单词
max_possible_forward = min(max_len, n - i) # 计算正向扫描的最大可能匹配长度
for l in range(max_possible_forward, 0, -1): # 从最大可能长度开始递减
candidate = text[i:i+l] # 提取当前候选单词
if candidate in dictionary: # 如果候选单词在词典中
forward_word = candidate # 找到匹配单词
break
# 反向扫描查找词
backward_word = '' # 存储逆向匹配的单词
max_possible_backward = min(max_len, i) # 计算逆向扫描的最大可能匹配长度
for l in range(max_possible_backward, 0, -1): # 从最大可能长度开始递减
candidate = text[i-l:i] # 提取当前候选单词
if candidate in dictionary: # 如果候选单词在词典中
backward_word = candidate # 找到匹配单词
break
# 判断选择哪个词
if len(forward_word) >= len(backward_word): # 如果正向匹配的单词长度大于等于逆向匹配的单词
result.append(forward_word) # 选择正向匹配的单词
i += len(forward_word) # 更新扫描位置
else: # 否则选择逆向匹配的单词
result.append(backward_word) # 选择逆向匹配的单词
i -= len(backward_word) # 更新扫描位置
return result # 返回分词结果
总结
precompute_max_length
:计算词典中最长单词的长度,用于优化分词过程中的最大匹配长度。forward_segmentation
:正向最大匹配分词算法,从左到右扫描文本,优先匹配最长的单词。backward_segmentation
:逆向最大匹配分词算法,从右到左扫描文本,优先匹配最长的单词。bidirectional_segmentation
:双向最大匹配分词算法,结合正向和逆向匹配,选择匹配长度更长的单词,以提高分词的准确性。