ZOJ 3328 Searching the String (AC自动机)

来源:互联网 发布:淘宝网店转让有风险吗 编辑:程序博客网 时间:2024/05/16 07:48

题目大意

  • 输入一个文本串和若干模板串。统计每个模板串在文本串中出现的次数。
  • 模板串有0和1两种类型,0型在文本串中可以重叠,1型的不可以重叠。

分析

  • 把所有模板串建成一个AC自动机
  • 求出字典树上所有串以0型和1型在文本串种出现的次数,即cnt[i][j]表示根节点到节点i的串在文本串中以类型j出现的次数
  • wordend[i]表示第i个模板串的在字典树的最后一个节点
  • 所以 ans[i] = cnt[wordend[i]][type[i]]
  • 注意:对于每个1型的串,只有当前位置到最后一次出现的位置的长度大于该串的长度时,才能对类型1计数+1

代码

/* cnt[i][j]表示根节点到节点i的串在文本串中以类型j出现的次数 * wordend[i]表示第i个模板串的在字典树的最后一个节点 * 所以 ans[i] = cnt[wordend[i]][type[i]] */#include <iostream>#include <string>#include <cstring>#include <queue>#include <cstdio>using namespace std;const int maxn = 600010;const int sigma_size = 26;int cnt[maxn][2] , wordend[maxn];struct AC {    int ch[maxn][sigma_size];    int dep[maxn];//dep[i]表示节点i的深度    int sz;    void Init() {        sz = 1; dep[0] = 0;        memset(ch[0],0 ,sizeof(ch[0]));    }    int idx(char c) {return c - 'a';}    void Insert(string const &s , int v)    {        int cur = 0 , len = s.length();        for(int i = 0; i < len; i++) {            int c = idx(s[i]);            if(!ch[cur][c]) {                memset(ch[sz] , 0 , sizeof(ch[sz]));                dep[sz] = dep[cur] + 1;                ch[cur][c] = sz++;            }            cur = ch[cur][c];        }        wordend[v] = cur;    }    int f[maxn];    void getFail()    {        queue<int> Q;        f[0] = 0;        for(int i = 0; i < sigma_size; i++) {            int u = ch[0][i];            if(u) {Q.push(u); f[u] = 0;}// last[u] = 0;}        }        while(!Q.empty()) {            int cur = Q.front(); Q.pop();            for(int i = 0; i < sigma_size; i++) {                int u = ch[cur][i];                if(!u) {ch[cur][i] = ch[f[cur]][i]; continue;}                Q.push(u);                int j = f[cur];                while(j && !ch[j][i]) j = f[j];                f[u] = ch[j][i];            }        }    }    int last[maxn]; //last[i]表示节点i最后一次在s中出现的位置    void find(string const &s) {        for(int i = 0; i < sz; i++) {            cnt[i][0] = cnt[i][1] = 0;            last[i] = -1;        }        int len = s.length() , cur = 0;        for(int i = 0; i < len; i++) {            int c = idx(s[i]);            cur = ch[cur][c];            int tmp = cur;            while(tmp) {                cnt[tmp][0]++;                /* 只有当前位置到最后一次出现的位置                 * 的长度大于该串的长度时,才能对类型1计数+1                 */                if(i - last[tmp] >= dep[tmp]) {                    cnt[tmp][1]++;                    last[tmp] = i;                }                tmp = f[tmp];            }        }    }};AC ac;const int maxs = 100010;char s[maxs] , str[maxs];int type[maxn];int main(){    int caseno = 1 , n;    while(scanf("%s" , str) != EOF) {        ac.Init();        cin >> n;        for(int i = 1; i <= n; i++) {            scanf("%d %s" , &type[i] , s);            ac.Insert(s , i);        }        ac.getFail();        ac.find(str);        //cout << "Case " << caseno++ << endl;        printf("Case %d\n" , caseno++);        for(int i = 1; i <= n; i++)            printf("%d\n" , cnt[wordend[i]][type[i]]);        printf("\n");    }    return 0;}
0 0