C++ 中的 Aho-Corasick 算法用于模式搜索


在这个问题中,我们给定一个输入字符串和一个数组 arr[]。我们的任务是在字符串中查找数组中所有单词的所有出现位置。为此,我们将使用 **Aho-Corasick 算法进行模式搜索**。

字符串和模式搜索是编程中一项重要内容。在编程中,算法越好,其实际应用就越多。**Aho-Corasick 算法**是一种 *非常重要且强大的算法,它使字符串搜索变得容易*。它是一种字典匹配算法,可以同时匹配所有字符串。该算法使用 *Trie 数据结构* 来实现。

Trie 数据结构

Trie 是一种 *前缀树或数字搜索树,其中每条边都用某个字母标记(每条出边具有不同的字母)*。

让我们举个例子来理解 *Aho-Corasick* 算法

**输入**

string = "bheythisghisanexample"
arr[] = {"hey", "this", "is", "an", “example”}

**输出**

Word hey starts from 2
Word this starts from 5
Word is starts from 11
Word an starts from 13
Word example starts from 15

该算法的时间复杂度为 **O(N+L+Z)**,其中 N=输入字符串/文本的长度

L=关键字(数组中的单词)的长度

Z=匹配次数。

实现

Aho-Corasick 算法可以通过以下简单的步骤构建

  • 使用队列构建 Trie,以便我们可以将队列中的每个字符弹出作为“Trie”的节点。

  • 构建失效链接(后缀链接)作为数组,可以存储下一个和当前字符

  • 构建输出链接作为数组以存储匹配的单词

  • 构建遍历函数 (FindNextState) 来检查所有字符。

**失效链接(后缀链接)** - 当我们遇到无法继续读取字符的字符串部分时,我们会通过遵循后缀链接回退,以尝试保留尽可能多的上下文。简而言之,它存储当 Trie 中当前字符没有边时所遵循的所有边。

**输出链接** - 它始终指向对应于当前状态中存在的最长单词的节点,我们确保使用输出链接将所有模式链接在一起。

示例

在线演示

#include<iostream>
#include <string.h>
#include<algorithm>
#include<queue>
using namespace std;
const int MaxStates = 6 * 50 + 10;
const int MaxChars = 26;
int OccurenceOfWords[MaxStates];
int FF[MaxStates];
int GotoFunction[MaxStates][MaxChars];
int BuildMatchingMachine(const vector<string> &words, char lowestChar = 'a', char highestChar = 'z'){
   memset(OccurenceOfWords, 0, sizeof OccurenceOfWords);
   memset(FF, -1, sizeof FF);
   memset(GotoFunction, -1, sizeof GotoFunction);
   int states = 1;
   for (int i = 0; i < words.size(); ++i){
      const string &keyword = words[i];
      int currentState = 0;
      for (int j = 0; j < keyword.size(); ++j){
         int c = keyword[j] - lowestChar;
         if (GotoFunction[currentState][c] == -1){
            GotoFunction[currentState][c] = states++;
         }
         currentState = GotoFunction[currentState][c];
      }
      OccurenceOfWords[currentState] |= (1 << i);
   }
   for (int c = 0; c < MaxChars; ++c){
      if (GotoFunction[0][c] == -1){
         GotoFunction[0][c] = 0;
      }
   }
   queue<int> q;
   for (int c = 0; c <= highestChar - lowestChar; ++c){
      if (GotoFunction[0][c] != -1 && GotoFunction[0][c] != 0){
         FF[GotoFunction[0][c]] = 0;
         q.push(GotoFunction[0][c]);
      }
   }
   while (q.size()){
      int state = q.front();
      q.pop();
      for (int c = 0; c <= highestChar - lowestChar; ++c){
         if (GotoFunction[state][c] != -1){
            int failure = FF[state];
            while (GotoFunction[failure][c] == -1){
               failure = FF[failure];
            }
            failure = GotoFunction[failure][c];
            FF[GotoFunction[state][c]] = failure;
            OccurenceOfWords[GotoFunction[state][c]] |= OccurenceOfWords[failure];
            q.push(GotoFunction[state][c]);
         }
      }
   }
   return states;
}
int FindNextState(int currentState, char nextInput, char lowestChar = 'a'){
   int answer = currentState;
   int c = nextInput - lowestChar;
   while (GotoFunction[answer][c] == -1){
      answer = FF[answer];
   }
   return GotoFunction[answer][c];
}
vector<int> FindWordCount(string str, vector<string> keywords, char lowestChar = 'a', char highestChar = 'z') {
   BuildMatchingMachine(keywords, lowestChar, highestChar);
   int currentState = 0;
   vector<int> retVal;
   for (int i = 0; i < str.size(); ++i){
      currentState = FindNextState(currentState, str[i], lowestChar);
      if (OccurenceOfWords[currentState] == 0)
         continue;
      for (int j = 0; j < keywords.size(); ++j){
         if (OccurenceOfWords[currentState] & (1 << j)){
            retVal.insert(retVal.begin(), i - keywords[j].size() + 1);
         }
      }
   }
   return retVal;
}
int main(){
   vector<string> keywords;
   keywords.push_back("All");
   keywords.push_back("she");
   keywords.push_back("is");
   string str = "Allisheall";
   cout<<"The occurrences of all words in the string ' "<<str<<" ' are \n";
   vector<int> states = FindWordCount(str, keywords);
   for(int i=0; i < keywords.size(); i++){
      cout<<"Word "<<keywords.at(i)<<' ';
      cout<<"starts at "<<states.at(i)+1<<' ';
      cout<<"And ends at "<<states.at(i)+keywords.at(i).size()+1<<endl;
   }
}

输出

The occurrences of all words in the string ' Allisheall ' are
Word All starts at 5 And ends at 8
Word she starts at 4 And ends at 7
Word is starts at 1 And ends at 3

更新于:2020-08-06

677 次浏览

开启您的 职业生涯

完成课程获得认证

开始学习
广告