[TJOI2013] 单词 - AC自动机

给定一篇由 \(n\) 个单词构成的文章,问每个单词以子串的形式出现了多少次。\(n\leq 200, len \leq 10^6\)

Solution

对所有单词建立 AC 自动机,再把它们扔上去跑,每走到一个位置就对该位置计数器 \(+1\),最后答案就是对应节点的子树和,于是将所有计数标记沿着 fail 树上传即可

#include <bits/stdc++.h>
using namespace std;

const int N = 2000005;

queue <int> q;
int c[N][26],val[N],fi[N],cnt,tag[N];
vector <int> g[N];
void ins(char *str,int id) {
    int len=strlen(str), p=0;
    for(int i=0; i<len; i++) {
        int v=str[i]-'a';
        if(!c[p][v]) c[p][v]=++cnt;
        p=c[p][v];
    }
    val[id]=p;
}
void build() {
    for(int i=0; i<26; i++) if(c[0][i]) fi[c[0][i]]=0, q.push(c[0][i]);
    while(!q.empty()) {
        int u=q.front();
        q.pop();
        for(int i=0; i<26; i++)
            if(c[u][i]) fi[c[u][i]]=c[fi[u]][i], q.push(c[u][i]);
            else c[u][i]=c[fi[u]][i];
    }
}
int query(char *s) {
    int len=strlen(s);
    int p=0;
    for(int i=0; i<len; i++) {
        p=c[p][s[i]-'a'];
        tag[p]++;
    }
}

int n;
string s[205];
char str[N];

void dfs(int p) {
    for(int q:g[p]) {
        dfs(q);
        tag[p]+=tag[q];
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++) {
        cin>>s[i];
        for(int j=0;j<s[i].length();j++) str[j]=s[i][j];
        str[s[i].length()]=0;
        ins(str,i);
    }
    build();
    for(int i=1;i<=n;i++) {
        for(int j=0;j<s[i].length();j++) str[j]=s[i][j];
        str[s[i].length()]=0;
        query(str);
    }
    for(int i=1;i<=cnt;i++) g[fi[i]].push_back(i);
    dfs(0);
    for(int i=1;i<=n;i++) cout<<tag[val[i]]<<endl;
}

猜你喜欢

转载自www.cnblogs.com/mollnn/p/12449797.html
今日推荐