首先对匹配算法有一个初步的认识
匹配算法首先要有一个匹配词典,然后要有一段话,看这段话里面能够拆分出多少个词典中的词。由于一段话里面的词语会有交叉,所以在两个词的交界处可能出现不符合句意的词被框出来,所以需要对匹配算法进行约束从而使一句话依据词典匹配出来的词不重复也不遗漏,这就是匹配算法中“正向、最大”这些条件。
不管是什么匹配算法,其核心都是如下几步:
确定词典的词匹配原则:一般来讲都是匹配词时按照最长词优先匹配。举个例子就是,如果有5个字的词在词典和匹配段里同时出现,则把这5个字的词先筛选(利用in实现)出来,而不去筛选从相同位置也能够成功匹配的4个字的词、3个字的词……匹配成功之后,匹配句中开始筛选的位置移动到下一位(代码中利用for循环进行实现)。到最后,如果没有筛选出来,则输出筛选位置的字,然后移动到下一位进行继续筛选。
正向最大匹配算法
def cutA(sentence):
result = []
sentenceLen = len(sentence)
n = 0
while n < sentenceLen:
matched = 0
for i in range(maxDictA, 0, -1):
piece = sentence[n:n+i]
if piece in dictA:
result.append(piece)
matched = 1
n = n + i
break
if not matched:
result.append(sentence[n])
n += 1
print(result)
# 测试代码
if __name__ == '__main__':
dictA = ['南京市', '南京市长', '长江大桥', '大桥']
maxDictA = max([len(word) for word in dictA])
sentence = "南京市长江大桥"
cutA(sentence)
下面是debug的每一步的详细解释(蓝色表示马上运行到了这一行还没进入这一行):
这一步导入了所需要的参数:
result用于存储最后的匹配结果,sentenceLen和n两个变量通过n的变化来标记筛选位置,并进行匹配位置的变化。
matched代表这个位置是否进行了匹配
这个for循环就是实现了“最大”匹配,先从词典中最长的词(maxDictA对应最长的词长度)开始匹配,这个piece就是根据最大长度(后面就依次递减),切分出对应长度的词,看看此切分后的词段是否能够在词典中找到有词能够对应(in操作),如果有,则将这个词存在列表result中,然后更新开始匹配的位置也就是n,跳出for循环进行下一个位置的匹配。如没有,就说明起码开始匹配位置的第一个词是不匹配的,那就移动到下一个位置进行匹配。并且i和n的变化始终的和是一致的,说明每次不移动首先都是将最大的maxDictA内部进行匹配完成再进行下一个位置最大maxDictA的从新匹配。
逆向最大匹配算法
思路和正向一样,就是把变化的位置调到了从后到前。这里采用了word的方式来存储和判断要匹配的词,而不是用piece直接表示。
def cutB(sentence):
result = []
sentenceLen = len(sentence)
while sentenceLen > 0:
word = ''
for i in range(maxDictB, 0, -1):
piece = sentence[sentenceLen-i:sentenceLen]
if piece in dictB:
word = piece
result.append(word)
sentenceLen -= i
break
if word is '':
sentenceLen -= 1
result.append(sentence[sentenceLen])
print(result[::-1])
如果想和正向匹配算法完全一样,可以参考下面的:
def cutB(sentence, dictA):
"""
反向最大匹配算法
"""
max_len = max([len(word) for word in dictA])
sentence_len = len(sentence)
result = []
n = sentence_len
while n > 0:
matched = False
for i in range(max_len, 0, -1):
piece = sentence[n - i:n]
if piece in dictA:
result.insert(0, piece) # 在列表头部插入匹配到的词
matched = True
n -= i
break
if not matched:
result.insert(0, sentence[n - 1])
n -= 1
return result
双向最大匹配算法
双向最大匹配算法是一种中文分词算法,结合了正向最大匹配算法和逆向最大匹配算法的优点,通过同时从句子的两端开始匹配,然后选择分词数量较少的结果作为最终输出。这种算法的优点在于可以有效地处理一些歧义情况,提高分词的准确性。
具体步骤如下:
- 正向最大匹配算法:从句子的开头开始,选择词典中最长的词进行匹配,然后将匹配到的词从句子中去除,继续匹配剩余部分,直到句子匹配完成。
- 逆向最大匹配算法:从句子的末尾开始,选择词典中最长的词进行匹配,然后将匹配到的词从句子中去除,继续匹配剩余部分,直到句子匹配完成。
- 比较结果:将正向最大匹配算法和逆向最大匹配算法得到的分词结果进行比较,选择分词数量较少的结果作为最终输出。
双向最大匹配算法的优点在于可以有效地处理一些特殊情况,例如一词多义、新词、未登录词等,提高了分词的准确性和鲁棒性。同时,双向最大匹配算法也相对简单高效,适用于大部分中文分词场景。
在实际应用中,双向最大匹配算法通常会结合其他技术和优化策略,例如词典树、未登录词处理、词性标注等,以进一步提高分词的效果和性能。
def cutA(sentence, dictA):
"""
正向最大匹配算法
"""
max_len = max([len(word) for word in dictA])
sentence_len = len(sentence)
result = []
n = 0
while n < sentence_len:
matched = False
for i in range(max_len, 0, -1):
piece = sentence[n:n + i]
if piece in dictA:
result.append(piece)
matched = True
n += i
break
if not matched:
result.append(sentence[n])
n += 1
return result
def cutB(sentence, dictA):
"""
反向最大匹配算法
"""
max_len = max([len(word) for word in dictA])
sentence_len = len(sentence)
result = []
n = sentence_len
while n > 0:
matched = False
for i in range(max_len, 0, -1):
piece = sentence[n - i:n]
if piece in dictA:
result.insert(0, piece) # 在列表头部插入匹配到的词
matched = True
n -= i
break
if not matched:
result.insert(0, sentence[n - 1])
n -= 1
return result
def bidirectional_cut(sentence, dictA):
"""
双向最大匹配算法
"""
fwd_result = cutA(sentence, dictA)
bwd_result = cutB(sentence, dictA)
# 选择分词数量较少的结果
if len(fwd_result) > len(bwd_result):
return bwd_result
else:
return fwd_result
# 测试代码
if __name__ == '__main__':
dictA = ['南京市', '南京市长', '长江大桥', '大桥']
sentence = "南京市长江大桥"
result = bidirectional_cut(sentence, dictA)
print(result)