背景
ac自动机是为了多模式串匹配,它能在O(n)时间范围内完成多个关键词在一个文档中的快速检索。
其主要原理是应用了 某个关键词的后缀和另一个关键词的前缀相等,那么没有完成关键词1的匹配时就可以直接跳转到另一个关键词的前缀之后开始匹配。
这一跳转主要由字典树中的fail指针完成。
其中fail指针建立的过程通过BFS的方式遍历字典树,当某个节点A的父节点是root时,fail指向父节点。根据父节点->fail转移->fail包含和A相等值的子节点B,把A的fail指向B。
其中检索的过程,发现当前字符char不在p的子节点中时,p指向p的fail,再次检查p是否包含char值的子节点。直到找到包含char的子节点或者p为空。
当char在p的子节点中时,p指向子节点。
当char不在p的子节点时,p指向根节点。
如果发现当前p是在某个关键词的最后一个节点的时候,把关键词所在的下标返回。
代码
class TrieNode:
def __init__(self,val):
self.val = val
self.fail = None
self.child = {}
self.tail = 0
class Trie:
def __init__(self):
self.root = TrieNode(None)
self.count = 0
def insert(self,word):
root = self.root
for c in word:
if c not in root.child:
t = TrieNode(c)
root.child[c] = t
root = root.child[c]
root.tail = len(word)
def build(self):
queue = [self.root]
while queue:
r = queue.pop(0)
for c in r.child:
cnode = r.child[c]
if r == self.root:
cnode.fail = self.root
else:
p = r.fail
while p:
if c in p.child:
cnode.fail = p.child[c]
break
p = p.fail
if not p:
cnode.fail = self.root
queue.append(cnode)
def search(self,text):
p = self.root
res = []
for i in range(len(text)):
char = text[i]
## fail转移
while p!=self.root and char not in p.child:
p = p.fail
## child转移
if char in p.child: ## p是root或者其他节点中包含char
p = p.child[char]
else: # 不包含char
p = self.root
if p.tail >0:
res.append((i-p.tail+1,p.tail))
return res
T = Trie()
for w in ["潮汕砂","砂锅粥"]:
T.insert(w)
T.build()
print(T.search("潮汕砂锅粥"))