AC自动机的python实现

前段时间在跟师兄打机器学习比赛的时候,师兄让我去匹配1.5w标签在20w数据中的出现次数,一开始的时候我用正则表达式,双重循环遍历1.5w标签和20w数据,粗略计算了一下,发现全部标签跑完需要大概6天的时间(这就很让我绝望啊)。后来师兄告诉我去用AC自动机,我看了一个下午还是没有实现AC自动机,无奈师兄只能抽空完成了我的任务。。。

经过几天摸索,我粗略实现了AC自动机。

首先介绍一下什么是AC自动机:Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法。多模匹配算法,完美契合我的任务(多标签匹配问题)
简单地讲,AC自动机就是字典树+kmp算法+失配指针

python建立字典树(实际上就是建树),由于python没有结构体,所以树的节点就用一个类来表示:

class node(object):

    def __init__(self):
        self.next = {}       #相当于指针,指向树节点的下一层节点
        self.fail = None     #失配指针,这个是AC自动机的关键
        self.isWord = False  #标记,用来判断是否是一个标签的结尾
        self.word = ""       #用来储存标签

接下来就是定义一个AC自动机的类:

class ac_automation(object):

    def __init__(self):
        self.root = node()  #定义根节点

建立字典树:

def add(self, word):
        temp_root = self.root
        for char in word:                           #遍历标签的每个字
            if char not in temp_root.next:          #如果节点下没有这个字,就加入这个字的节点
                temp_root.next[char] = node()       
            temp_root = temp_root.next[char]        #沿着标签建立字典
        temp_root.isWord = True                     #标签结束,表示从根到这个节点是一个完整的标签
        temp_root.word = word                       #储存这个标签

创建fail指针:

def make_fail(self):
        temp_que = []                                    #使用BFS来遍历这棵字典树
        temp_que.append(self.root)
        while len(temp_que) != 0:
            temp = temp_que.pop(0)
            p = None
            for key,value in temp.next.item():
                if temp == self.root:                     #根节点的孩子们的fail都指向根
                    temp.next[key].fail = self.root
                else:
                    p = temp.fail
                    while p is not None:                  #寻找fial指针指向的地方   
                        if key in p.next:                 #如果是另一个fail指针下面找到了,就储存
                            temp.next[key].fail = p.fail
                            break
                        p = p.fail
                    if p is None:                         #如果该节点fail指针不存在,就指向根
                        temp.next[key].fail = self.root
                temp_que.append(temp.next[key])

多模匹配:

def search(self, content):
        p = self.root
        result = []
        currentposition = 0       #用来标记当前标签的汉字

        while currentposition < len(content):
            word = content[currentposition]
            while word in p.next == False and p != self.root:    #搜索状态机,直到匹配
                p = p.fail

            if word in p.next:
                p = p.next[word]
            else:
                p = self.root

            if p.isWord:                 #若状态到达标签结尾,就添加入结果中
                result.append(p.word)

            currentposition += 1

把上面3个结合起来就是一个AC自动机的类了:

class ac_automation(object):

    def __init__(self):
        self.root = node()

    def add(self, word):
        temp_root = self.root
        for char in word:
            if char not in temp_root.next:
                temp_root.next[char] = node()
            temp_root = temp_root.next[char]
        temp_root.isWord = True
        temp_root.word = word

    def make_fail(self):
        temp_que = []
        temp_que.append(self.root)
        while len(temp_que) != 0:
            temp = temp_que.pop(0)
            p = None
            for key,value in temp.next.item():
                if temp == self.root:
                    temp.next[key].fail = self.root
                else:
                    p = temp.fail
                    while p is not None:
                        if key in p.next:
                            temp.next[key].fail = p.fail
                            break
                        p = p.fail
                    if p is None:
                        temp.next[key].fail = self.root
                temp_que.append(temp.next[key])

    def search(self, content):
        p = self.root
        result = []
        currentposition = 0

        while currentposition < len(content):
            word = content[currentposition]
            while word in p.next == False and p != self.root:
                p = p.fail

            if word in p.next:
                p = p.next[word]
            else:
                p = self.root

            if p.isWord:
                result.append(p.word)

            currentposition += 1
        return result

测试:

ac = ac_automation()
ac.add('潮汕话')
ac.add('垃圾话')
ac.add('666')
ac.add('生存游戏')


ac.search('说潮汕话的人不要轻易喷垃圾话,有时间还不如玩玩生存类的游戏')
['潮汕话', '垃圾话']

AC自动机真是个好东西!

参考:https://github.com/metadata1984/pyAhocorasick

你可能感兴趣的:(机器学习)