AC自动机-字符串处理

AC自动机

全称:Aho-Corasick automation
结构:Trie树 + 失败指针fail
应用:多模式串匹配
优点:结合Trie树的查询特点和KMP的失配转移,加快多模式串匹配速度

AC自动机特点

Trie树的基础上扩展,插入查询与Trie树类似
每个节点建立了失配指针fail,与KMP的next类似
匹配过程
当前匹配成功时,与Trie树的查询类似
当前匹配失败时,通过fail实现类似与KMP的next效果,转移到fail指向

的节点
AC自动机-字符串处理_第1张图片
AC自动机性质

根节点root的fail指针,指向自己
其他节点fail指针,指向后缀子串的节点
若不存在相同后缀的节点,则fail指向root

失败指针的含义

一个模式串匹配失败的时候
跳到它的失败指针上继续匹配
满足的性质:
它指向的一定是某个串的前缀
这个前缀是当前节点串的后缀
且一定是最长后缀


AC自动机的实现

定义含有fail指针的Trie树

指向子节点的指针
当前节点的值
匹配失败时,指向下一个位置fail指针

struct Trie
{
    int root, L;  // root - 根节点下标, L – 节点数目
    int ch[MAX][26], fail[MAX], v[MAX];  // fail[i] – 失败指针
    int newnode(); // 新节点初始化,返回下标
    void init();    // 字典树初始化
    void Insert(char str[]); // 插入字符串str
    void Build();  // 构造fail指针
    int query();     // 匹配多模式串,返回成功匹配数目
    void debug();  // 调试函数
}ac; // 设置变量


初始化

int Trie::newnode()
{
    v[L] = 0;
    memset(ch[L], -1, sizeof(ch[L]));
    return L++;
}
void Trie::init()
{
    L = 0; root = newnode();
}

插入过程
从根节点开始,按照字母对应节点不断向下
直到单词结束,在该节点上记录单词信息

void Trie::Insert(char str[])
{
    int now = root;
    for(int i = 0; str[i]; i++)
    {
        if(ch[now][str[i]-'a'] == -1)
            ch[now][str[i]-'a'] = newnode();
        now = ch[now][str[i]-'a'];
    }
    v[now] ++;
}

构建fail指针
将root的fail指针指向自己
然后使用BFS构造其他fail指针
队列初始放入root的所有子节点,fail为root
逐步处理队列节点
不存在子节点i,则该子节点,指向当前节点的fail对象的子节点i
存在,则将该子节点fail指针,指向当前节点的fail对象的子节点i

void Trie::Build()
{
    int now;
    queue<int> q;
    fail[root] = root;
    for(int i = 0; i<26; i++)
    {
          if(ch[root][i] == -1)
           { ch[root][i] = root;}
          else
          {
            fail[ch[root][i]] = root;
            q.push(ch[root][i]);
           }
     }
     while(!q.empty())
     {
         now = q.front(); q.pop();
         for(int i = 0; i<26; i++)
         {
            if(ch[now][i] == -1)
            { ch[now][i] = ch[fail[now]][i]; }
            else
            {
               fail[ch[now][i]] = ch[fail[now]][i];
               q.push(ch[now][i]);
            }
         }
     }
 }


查询匹配

从根节点开始搜索
得到第一个字母节点后,转到对应子树
若匹配失败,则转到fail指向的节点
在相应子树继续搜索下一个字母
重复上述操作,直到单词结束

每经过一个节点,沿着该节点fail指针向上遍历,直到根节点
将经过的所有节点的信息记录

int Trie::query()
{
    int res = 0;
    int now = root, tmp;
    for(int i = 0; s[i]; i++)
    {
        tmp = now = ch[now][s[i]-'a']; 
                // 沿着fail向上遍历后缀所有节点
        while(tmp != root)
        {
            res += v[tmp];
            v[tmp] = 0;
            tmp = fail[tmp]; 
        }
    }
    return res;
}




你可能感兴趣的:(AC自动机-字符串处理)