bzoj3172(ac自动机)

来源:互联网 发布:单片机芯片破解 编辑:程序博客网 时间:2024/05/24 04:56

先加入到trie

再计算所有的单词数,每加入一个单词更新他的子串cnt++

找到单词位置,输出答案

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<cstdlib>#include<queue>using namespace std; char wd[205][80050];int ch[1000005][30],n,tot,val[1000005],fail[1000005],last[1000005],cnt[1000005];inline int idx(char ch) {return ch-'a';}void insert(char *s){    int k=0,len=strlen(s);    for (int i=0;i<len;i++)    {        int id=idx(s[i]);        if (!ch[k][id]) ch[k][id]=++tot;        k=ch[k][id];    }    val[k]=1;//标记是单词节点 }void getfail(){    queue<int> q;    for (int i=0;i<26;i++)     if (ch[0][i]) q.push(ch[0][i]);    while (!q.empty())    {        int v,u=q.front();q.pop();        for (int i=0;i<26;i++)        if (v=ch[u][i])        {            int j=fail[u];            while (!ch[j][i]&&j) j=fail[j];            if (ch[j][i]) j=ch[j][i];            fail[v]=j;            if (val[fail[v]]) last[v]=fail[v];else last[v]=last[fail[v]];            q.push(v);        }    }}void print(int k){    if (!k) return ;    cnt[k]++;print(last[k]);}void find(char *s){    int k=0,len=strlen(s);    for (int i=0;i<len;i++)    {        int id=idx(s[i]);        k=ch[k][id];        if (val[k]) print(k);else print(last[k]);//如果是单词节点,从这个开始,否则从它的最长后缀单词节点开始 //对于每一个单词,标记其所有子串cnt++;出现次数++,这里的子串就是在这个位置的前缀的所有合法的后缀,last存上一个合法后缀    }}void getans(char *s){    int k=0,len=strlen(s);    for (int i=0;i<len;i++)    {        int id=idx(s[i]);        k=ch[k][id];    }    printf("%d\n",cnt[k]);//找到单词位置,输出出现次数,cnt表示出现次数 }int main(){    scanf("%d",&n);    for (int i=1;i<=n;i++)    {        scanf("%s",wd[i]);        insert(wd[i]);    }    getfail();    for (int i=1;i<=n;i++) find(wd[i]);//标记子串次数++     for (int i=1;i<=n;i++) getans(wd[i]);    return 0;} 







1 0
原创粉丝点击