正、反向分词算法
Table of Contents
1. 反向最大匹配分词
假设要分割句子 S:
我在别人那里听说你是黑客
分割出的结果应该是:
我/在/别人/那里/听说/你/是/黑客
首先反向取出最多 N 个字,这里 N=5,所以第一次取出“说你是黑客” 然后再将这句话逐步带入词典查询,如果没找到,就去掉最后个字。整个过程中会产生两个结果:
结果1,找到这个词了,那么这句话的字符串变成这个词余下的字符串;
结果2,没有找到,但必须将最后一个字作为一个单独的词分割出来(往往是“的”、“是”之类的字)
这句话的分割过程如下:
1.1. 第一轮
1 说你是黑客-> 没有找到 2 你是黑客-> 没有找到 3 是黑客-> 没有找到 4 黑客-> 找到
1.2. 第二轮
S = 里听说你是
1 里听说你是-> 没有找到 2 说你是-> 没有找到 3 你是-> 没有找到 4 是 -> 遇到剩下一个字,所以单独成为一个词
1.3. 第三轮
S = 那里听说你
1 那里听说你-> 没有找到 2 里听说你-> 没有找到 3 听说你-> 没有找到 4 说你-> 没有找到 4 你 -> 遇到剩下一个字,所以单独成为一个词
1.4. 第四轮
S = 人那里听说
1 人那里听说-> 没有找到 2 那里听说-> 没有找到 3 里听说-> 没有找到 4 听说-> 找到,进入第五轮
1.5. 第五轮
S = 在别人那里
1 在别人那里-> 没有找到 2 别人那里-> 没有找到 3 人那里-> 没有找到 4 那里-> 找到,进入第六轮
1.6. 第六轮
S = 我在别人
1 我在别人-> 没有找到 2 在别人-> 没有找到 3 别人-> 找到,进入第七轮
1.7. 第七轮
S = 我在
1 我在-> 没有找到 2 在 -> 只剩下一个字,所以单独作为一个词
1.8. 第八轮
S = 我
1 我-> 仅剩一个字,结束分词
1.9. 实现代码
# create date: 2013-06-23 # last update: 2013-06-23 word_dict = list() def load_dict(): """ 将词典加载到内存中 """ # 需要自己去找一份字典文件,命名为 words.dic with open('words.dic', 'r') as f: for line in f: line = line.strip() line = line.decode('utf-8') word_dict.append(line) def split_words(words): max_len = 5 # 每次取 max_len 个字 split_result = list() # 分词结果 while words: # 先取 max_len 个字出来分词 text = words[0 - max_len:] # 标志位,如果为 True 表示找到词了 is_find = False # 开始尝试分词 while not is_find: if text in word_dict: is_find = True split_result.insert(0, text) # 后移 words = words[:0-len(text)] # 如果只剩下一个字了,它单独成为一个词 # 比如“我的是他的”这句话里就没有一组词,所以要将它们分割成一个个字 # 这里参考函数注释中的“结果1” elif len(text) == 1: is_find = True split_result.insert(0, text) words = words[:-1] # 如果没有找到,就去掉第一位 else: text = text[1:] return split_result if __name__ == '__main__': load_dict() print('/'.join(split_word(u'我在别人那里听说你是黑客')))
2. 正向最大匹配分词
假设要分割句子 S:
我在别人那里听说你是黑客
分割出的结果应该是:
我/在/别人/那里/听说/你/是/黑客
首先取出最多 N 个字,这里 N=5,所以第一次取出“我在别人那” 然后再将这句话逐步带入词典查询,如果没找到,就去掉最后个字。整个过程中会产生两个结果:
结果1:找到这个词了,那么这句话的字符串变成这个词余下的字符串; 结果2:没有找到,但必须将最后一个字作为一个单独的词分割出来(往往是“的”、“是”之类的字)
这句话的分割过程如下:
2.1. 第一轮
1 我在别人那 => 没有找到 2 我在别人 => 没有找到 3 我在别 => 没有找到 4 我在 => 没有找到 5 我 => 只剩下一个字,这个字必须作为一个单独的词
2.2. 第二轮
在第一轮中,“我”作为单独一个词后,下一步就不需要查找这个词了,这时再取 5 个字(接着上次没有找到的地方开始取):
S = 在别人那里
1 在别人那里 => 没有找到 2 在别人那 => 没有找到 3 在别人 => 没有找到 4 在别 => 没有找到 5 在 => 又遇到剩下一个字,所以单独出现
2.3. 第三轮
S = 别人那里听
1 别人那里听 => 没有找到 2 别人那里 => 没有找到 3 别人那 => 没有找到 4 别人 => 找到,进入第四轮
2.4. 第四轮
S = 那里听说你
1 那里听说你 => 没有找到 2 那里听说 => 没有找到 3 那里听 => 没有找到 4 那里 => 找到,进入第五轮
2.5. 第五轮
S = 听说你是黑
1 听说你是黑 => 没有找到 2 听说你是 => 没有找到 3 听说你 => 没有找到 4 听说 => 找到,进入第六轮
2.6. 第六轮
S = 你是黑客
1 你是黑客 => 没有找到 2 你是黑 => 没有找到 3 你是 => 没有找到 4 你 => 只剩下一个字,所以单独作为一个词
2.7. 第七轮
S = 是黑客
1 是黑客 => 没有找到 2 是黑 => 没有找到 3 是 => 只剩下一个字,所以单独作为一个词
2.8. 第八轮
S = 黑客
1 黑客 => 找到,结束分词
2.9. 实现代码
只用简单修改上面的 splitwords 函数:
def split_word(words): max_len = 5 # 每次取 max_len 个字 split_result = list() # 分词结果 while(words): # 先取 max_len 个字出来分词 text = words[0: max_len] # 标志位,如果为 True 表示找到词了 is_find = False # 开始尝试分词 while not is_find: if text in word_dict: is_find = True split_result.append(text) # 后移 words = words[len(text):] # 如果只剩下一个字了,它单独成为一个词 # 比如“我的是他的”这句话里就没有一组词,所以要将它们分割成一个个字 # 这里参考函数注释中的“结果1” elif len(text) == 1: is_find = True split_result.append(text) words = words[1:] # 如果没有找到,就去掉最后一位 else: text = text[:len(text)-1] return split_result