AC自动机入门-poj3461解法

来源:互联网 发布:vb语言用处 编辑:程序博客网 时间:2024/05/19 15:19

前提 :
1. KMP 单模式匹配原理
2. 字典树结构

失配函数获取:
1. 找当前点s的失配节点下标为c, 先找到找父节点f的失配节点fail[f], 如果fail[f]存在下标为c子节点, 那么fail[s] = ch[fail[f]][c]
不存在沿着失配指针回推fail[….fail[f]]直至根节点
2. 由于是树形, 所以一般采取bfs递推fail值, 而不是KMP的线性递归
3. 实现过程 :
1) 根节点的子结点fail值全为0, 入队
2) 第三层开始, bfs入队, 回推fail(找父节点fail, KMP思想)
ps:还可以记下以该节点为尾的字符串的最大后缀串(模板式串中存在的)
比如如果有3个模式串AZAZAB, AZA, ZA那么 第一个串的最后一个‘A’的last值应该是AZA的最后一个‘A’的节点下标, 这也说明多模式串中同一个节点可能对应多个串的串尾

code:

struct AC_Auto{    int ch[10010][26], last[10010], val[10010], f[10010];    string tmp[10010];/**存节点字符串*/    int sz;    int idx(char s){return s - 'A';}    AC_Auto(){        memset(ch, 0, sizeof ch);        sz = 1;        memset(val, 0, sizeof val);        memset(last, 0, sizeof last);/**初始化*/        memset(f, 0, sizeof f);    }    void insert(string w){/**一般字典树建树*/        int u = 0;        for(int i = 0; w[i]; ++i){            int c = idx(w[i]);            if(!ch[u][c]){                ch[u][c] = sz++;            }            u = ch[u][c];        }        val[u] = 1;        tmp[u] = w;/**标记单词, 记下单词*/    }    void getFail(){        queue<int> q;        q.push(0);/**头结点*/        while(!q.empty()){            int t = q.front();            q.pop();            for(int c = 0; c < 26; ++c){                if(ch[t][c]){/**子节点*/                    int u = ch[t][c], v = f[t];                    q.push(u);                    while(v && !ch[v][c]) v = f[v];/**回推获取失配/                    /**这里如果退回到根节点, 根节点下标为c节点也可能存在*/                    f[u] = t ? ch[v][c] : 0;/**第二层直接fail = 0*/                    last[u] = val[f[u]] ? f[u] : last[f[u]];                    /**获取last*/                    cout << "u : " << u << " " << last[u] << endl;                }            }        }    }    int query(){        int u = 0;        for(int i = 0; s[i]; ++i){            int c = idx(s[i]);            while(u && !ch[u][c])   u = f[u];            u = ch[u][c];/**查询*/            if(val[u]) print(u);/**找到一个单词*/            else if(last[u]) print(last[u]);/**找到模式串中该节点表示字符串最大后缀的单词*/        }        return 1;    }    void print(int x){/**递归打印*/        if(x){            cout << tmp[x] << endl;            print(last[x]);        }    }};

POJ 3461 AC自动机解法

#include <iostream>#include <cstring>#include <queue>using namespace std;const int N = 1000010;char s[N], w[10010];struct AC_Auto{    int ch[10010][26], last[10010], val[10010], f[10010];    int sz;    int idx(char s){return s - 'A';}    AC_Auto(){        memset(ch, 0, sizeof ch);        sz = 1;        memset(val, 0, sizeof val);        memset(last, 0, sizeof last);        memset(f, 0, sizeof f);    }    void insert(){        int u = 0;        for(int i = 0; w[i]; ++i){            int c = idx(w[i]);            if(!ch[u][c]){                ch[u][c] = sz++;            }            u = ch[u][c];        }        val[u] = 1;    }    void getFail(){        queue<int> q;        q.push(0);        while(!q.empty()){            int t = q.front();            q.pop();            for(int c = 0; c < 26; ++c){                if(ch[t][c]){                    int u = ch[t][c], v = f[t];                    q.push(u);                    while(v && !ch[v][c]) v = f[v];                    f[u] = t ? ch[v][c] : 0;                    last[u] = val[f[u]] ? f[u] : last[f[u]];                }            }        }    }    int query(){        int u = 0, ans = 0;        for(int i = 0; s[i]; ++i){            int c = idx(s[i]);            while(u && !ch[u][c])   u = f[u];            u = ch[u][c];            if(val[u]) ++ans, u = f[u];/**找到之后回退失配节点, 同裸KMP*/        }        return ans;    }};int main(){    int t;    cin >> t;    while(t--){        cin >> w >> s;        AC_Auto ac;        ac.insert();        ac.getFail();        cout << ac.query() << endl;    }    return 0;}
0 0