您的位置:首页 > 编程语言 > Python开发

基于 double array 实现汉字的trie树索引 与 查询功能 python实现

2012-04-08 21:05 1196 查看
一、 基本原理。

基本原理:利用字符串集合中字符串的公共前缀来降低时间开销以达到提高效率的目的。
性质:1,根结点不包含任何字符信息;2,如果字符的种数为n(如英文的26个字母),则每个结点的出度为n(这样必然会导致浪费很多空间,这也是trie的缺点,我还没有想到好点的办法避免);3,查找,插入复杂度为O(n),n为字符串长度。
具体请百度之。

二、基于 trie字典的汉字串检索代码(python):

# coding=gbk
dicts = ['啊','阿根廷','阿胶','阿拉伯','阿拉伯人','埃及']
# 字后缀数组
sufarr = {'啊':[],'阿':['根','胶','拉'],'埃':['及'],
'根':['廷'],'胶':[],'拉':['伯'],'及':[],
'廷':[],'伯':['人'],'人':[]}
# 按ID编码
codes = [ '啊','阿','埃','根','胶','拉','及','廷','伯','人' ]
# 字的层次标示
level = [1, 1, 1, 2, 2, 2, 2, 3, 3, 4]

# 构建双数组
base = [0]*21           # len:21 ; 21=2*10+1 的素数
check = [0]*21
# 记录已确定的字的base索引
bid_mark = {'啊':0,'阿':1,'埃':2, '根':0,'胶':0,'拉':0,'及':0,'廷':0,'伯':0,'人':0}
base[:3]=[1]*3          # 首字部分,简单置为1
def pre_mark(pre_word,wi,word):
''' 判断base中wi下标的前缀都在pre_words词表直到首字 且word为wi下标对应的字 '''
bkeys = bid_mark.keys()
if word not in bkeys or bid_mark[word]!=wi:             # 当前字的base值不匹配
return False
pre_wi = check[wi]                     # 逆序遍历base的前缀
pid = -1                               # 逆序遍历字的前缀
while pre_wi!=0 and (-1)*pid<=len(pre_word):
if pre_word[pid] not in bkeys or bid_mark[pre_word[pid]]!=pre_wi:
return False
pre_wi = check[pre_wi]
pid -= 1
if (-1)*pid>len(pre_word): return True # 完成前缀的遍历
else: return False

for w in dicts:
cw = [ w[i:i+2] for i in range(0,len(w),2) ]            # 分割出字
for iid in range(1,len(cw)):
par_w = cw[iid-1]                   # 字的前缀
iid_w = cw[iid]                     # 当前的字
par_id = codes.index(par_w)         # 字的前缀的编码
wid = codes.index(iid_w)
bval = bid_mark[par_w]
#if iid_w=='及': print "ID: %d, word: %s, pre-wrod: %s, code: %d, pre-code: %d, pre-base:%d"%(
#    iid,iid_w,par_w,wid,par_id,bval)
for wi in range(base[bval]+wid,21):   # 遍历所有可能的base位置
# 找到其base-id,置位
p_mk = pre_mark(cw[:iid],wi,iid_w)
print "TEST::pre_mark() base-id: %d, word: %s, result: %s"%(wi,iid_w,str(p_mk))
if p_mk:                              # 该词及其前缀部分已经设置值
break
if (not base[wi] and not check[wi]):  # 该位置未使用或吻合前缀
check[wi] = bval
# 设置字的 base[] 值
for tval in range(1,21-wi):    # 遍历所有可能的取值
flag = True
for suf_w in sufarr[iid_w]:
chi_id = codes.index(suf_w)     # 字的后缀的编码
if  base[tval+chi_id]!=0 or check[tval+chi_id]!=0:
flag = False
break              # tval不满足child的base check 为0,再尝试其他值
if flag:
base[wi] = tval        # 成功,保存该base[]
if wi==6 or wi==-1 or wi==9:
print "ID: %d, word: %s, pre-word: %s, base: %d, base-val: %d, check: %d"%(
iid,iid_w,par_w,wi,tval,check[wi])
break
if not base[wi]:               # 无合适的 base
print "ERROR:: set %s's base[]" % iid_w
exit(1)
bid_mark[iid_w] = wi #; print "finnished one! %s=%d" % (iid_w,wi)
break                          # base check均成功
if not base[wi]:
print "ERROR:: get %s's position in base[]" % iid_w
exit(2)

# 修改词尾标志
for w in bid_mark.keys():
if len(sufarr[w])==0:
eid = bid_mark[w]
base[eid] *= -1

# 查询给定词
def next_check(wid, code):
''' 验证两个base[]索引id间是否有连接 '''
if code<0 or code>=len(codes):          # 字编码无效
return False
chiid = abs(base[wid])+code
if check[chiid]!=wid:
return False
return True
qw = dicts[1] ;     # 查询词
res = ''            # 返回结果
words=[ qw[w:w+2] for w in range(0,len(qw),2) ]
head_bid = bid_mark[words[0]] ; res = ''.join(words[0])
for w in words[1:]:
cod = codes.index(w)
if not next_check(head_bid,cod):
print "word: %s no find!" % qw
break
head_bid = abs(base[head_bid])+cod      # 更新id
res = ''.join([res,w])
if base[head_bid]<0:
print "find a word: %s in Search word: %s"%(res,qw)
参考: http://blog.csdn.net/joylnwang/article/details/6825991
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: