Python中模式搜索Aho-Corasick 算法

Aho-Corasick是一种字典匹配算法。该算法用于搜索关键字集中存在的单词。该算法可以快速有效地查找单词及其位置。 Aho-Corasick 算法构建了一个现有系统并采用了TRIE 概念。

树数据结构用于执行该技术。当我们创建树时,它会将其转换或尝试将其转换为自动机,从而使我们能够在线性时间内完成或执行搜索。

Aho-Corasick 算法的时间复杂度
Aho-Corasik 算法在O(X+ Y+ Z) 时间内搜索单词,其中X是文本长度,Y是关键字长度,Z是关键字在文本中出现的次数。

Ahio-Corasick 算法的问题陈述
假设我们有输入文本和一个包含 m 个单词的数组 a[ ]。我们需要搜索输入文本中存在的单词数。

设 x 为文本长度,y 为所有单词中的字符数。这意味着 y = len(a [0]) + len(a [1]) + len(a [2]) + …。 + 长度(a [z - 1])。这里,z是输入单词的数量。

我们将通过一个示例来理解该算法,在该示例中,我们将采用输入字符串和要在文本字符串中搜索的单词集。

例子:

输入:

文本字符串:txt=  “hellotheirshere”  
示例单词集: a[ ] = { "he" ,  "hello" ,  "she" ,  "here" ,  "their" ,  "the" }  

输出:

单词“he”位于索引 0 到 1 处。
单词“he”位于索引 6 到 7 处
单词“he”位于索引 11 到 12 处
单词“hello”位于索引 0 到 4 处。
单词“the”位于索引 5 到 7 处

their
”一词位于索引 7 至 11 处。
单词“she”位于索引 10 到 12 处。
单词“here”位于索引 11 到 14 处

算法的预处理
Aho-Corasick 算法分为三个不同的阶段:

  • Go To
  • Output
  • Failure

Go To转到:此阶段使用以模式输入算法的关键字来创建树。它转到主函数,然后设置点,然后转到主根。它跟踪数组 a[ ] 中所有单词的边距。二维数组 gt[ ][ ] 表示 go 函数,我们可以在其中存储字符和状态以用于进一步的下一个状态。

Output输出:该阶段搜索以特定状态结尾的单词。这是条件和可用性匹配时的结果。该函数存储以当前状态结尾的单词的索引。一维数组 op[] 表示输出函数,我们可以在其中将单词存储为当前状态的位图。

Failure失败:它搜索向后转换以从集合中找到合适的关键字。如果关键字不匹配,则不会计算在内。当当前字符在 Trie 中缺少边时,该函数记录其后面的每条边。一维数组 fl[ ] 表示我们记录当前状态的下一个状态的失败。

用于模式搜索的 Aho-Corasick 算法在 Python 中的实现
以下是 Aho-Corasick 算法的 Python 实现:

from collections import defaultdict  
class Aho_Corasick:  
    def __init__(self, words):  
        self.maxStates = sum([len(word) for word in words])  
        self.maxChar = 22  
        self.out = [0]*(self.maxStates + 1)  
        self.fail = [-1]*(self.maxStates + 1)  
        self.goto = [[-1]*self.maxChar for _ in range(self.maxStates + 1)]  
  
        for i in range(len(words)):  
            words[i] = words[i].lower()  
        self.words = words  
        self.states_count = self.__build_matching_machine()  
          
    def __build_matching_machine(self):  
        m = len(self.words)  
        state = 1  
  
        for i in range(m):  
            word = self.words[i]  
            currentState = 0  
  
            for char in word:  
                c = ord(char)  
  
                if self.goto[currentState][c] == -1:  
                    self.goto[currentState][c] = state  
                    state += 1  
                currentState = self.goto[currentState][c]  
        self.out[currentState] |= (1 << i)  
  
        for c in range(self.maxChar):  
            if self.goto[0][c] == -1:  
                self.goto[0][c] = 0  
            queue = []  
  
        for ch in range(self.maxChar):  
            if self.goto[0][c] != 0:  
                self.fail[self.goto[0][c]] = 0  
                queue.append(self.goto[0][c])  
  
        while queue:  
            states = queue.pop(0)  
  
            for c in range(self.maxChar):  
                if self.goto[states][c] != -1:  
                    failure = self.fail[states]  
  
            while self.goto[failure][c] == -1:  
                failure = self.fail[failure]  
            failure = self.goto[failure][c]  
            self.fail[self.goto[states][c]] = failure  
            self.out[self.goto[states][c]] |= self.out[failure]  
            queue.append(self.goto[states][ch])  
        return state  
  
        def find_Next_State(self, currentState, next_inp):  
            ans = currentSate  
            c = ord(next_inp) - 97   
            while self.goto[ans][c] == -1:  
                answer = self.fail[ans]  
   
            return self.goto[ans][c]  
  
        def search(self, txt):  
            txt = txt.lower()  
            currentSate = 0  
            res = defaultdict(list)  
  
            for i in range(len(txt)):  
                currentState = self.findNextState(currentState, txt[i])  
  
                if self.out[currentState] == 0: continue  
  
                for j in range(len(self.words)):  
                    if (self.out[currentState] & (1 << j)) > 0:  
                        word = self.words[j]  
                        res[word].append(i-len(word)+1)  
            return result  
  
if __name__ == "__main__":  
    words = [
"he", "hello", "she", "here", "their", "the"]  
    txt =
"hellotheirshere"  
    aho_chorasick = Aho_Corasick(words)  
    res = aho_chorasick.search(txt)  
  
    for word in result:  
        for i in result[word]:  
            print(
"The Word", word, "is found at index", i, "to", i + len(word) - 1)   


解释:

在这里,我们使用默认字典来存储输出。我们设置了字符和状态的最大限制。利用算法的三个阶段,我们对数据进行了预处理。然后,利用循环和队列,我们构建了机器/自动机,然后从文本字符串中搜索单词集。