# -*- coding: utf-8 -*-
class AhoNode:
''' Вспомогательный класс для построения дерева
'''
def __init__(self):
self.goto = {}
self.out = []
self.fail = None
def aho_create_forest(patterns):
'''Создать бор - дерево паттернов
'''
root = AhoNode()
for path in patterns:
node = root
for symbol in path:
node = node.goto.setdefault(symbol, AhoNode())
node.out.append(path)
return root
def aho_create_statemachine(patterns):
'''Создать автомат Ахо-Корасика.
Фактически создает бор и инициализирует fail-функции
всех узлов, обходя дерево в ширину.
'''
# Создаем бор, инициализируем
# непосредственных потомков корневого узла
root = aho_create_forest(patterns)
queue = []
for node in root.goto.itervalues():
queue.append(node)
node.fail = root
# Инициализируем остальные узлы:
# 1. Берем очередной узел (важно, что проход в ширину)
# 2. Находим самую длинную суффиксную ссылку для этой вершины - это и будет fail-функция
# 3. Если таковой не нашлось - устанавливаем fail-функцию в корневой узел
while len(queue) > 0:
rnode = queue.pop(0)
for key, unode in rnode.goto.iteritems():
queue.append(unode)
fnode = rnode.fail
while fnode is not None and key not in fnode.goto:
fnode = fnode.fail
unode.fail = fnode.goto[key] if fnode else root
unode.out += unode.fail.out
return root
def aho_find_all(s, root, callback):
'''Находит все возможные подстроки из набора паттернов в строке.
'''
node = root
for i in xrange(len(s)):
while node is not None and s[i] not in node.goto:
node = node.fail
if node is None:
node = root
continue
node = node.goto[s[i]]
for pattern in node.out:
callback(i - len(pattern) + 1, pattern)
############################
# Демонстрация работы алгоритма
def on_occurence(pos, patterns):
print "At pos %s found pattern: %s" % (pos, patterns)
patterns = ['пизд', 'прошм', 'ебл', 'шмонька', 'хуй', 'сука', 'пидар']
s = "пиздопр0шмондовочкаибливая и сукаХхххуй ебал пидар пидор сYка пNдорас"
root = aho_create_statemachine(patterns)
aho_find_all(s, root, on_occurence)