bzoj3172: [Tjoi2013]单词

AC自动机.

统计每个字符串在自己和其他字符串中出现的次数。

ac自动机的概念,首先有个trie树保存了所有的字符串。

fail指针指向该字符串的后缀在整个trie树中可以做最长的前缀的位置。

每个字符串都可以由fail指针转移到是该字符串字串的位置。

所以很多统计就可以进行辣。

字符串的题好难。。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1000000 + 10;

struct AC {
    int a[maxn][26],sum[maxn],q[maxn],fail[maxn]; 
    int cnt,l,r,p;
    char s[maxn];
    void insert(int &p) {
        scanf("%s",s);
        int len=strlen(s);    
        p=1;
        for(int i=0;i<len;i++) {
            int c=s[i]-'a';
            if(!a[p][c]) a[p][c]=++cnt;
            p=a[p][c];
            sum[p]++;
        }
    }
    
    void get_fail() {
        l=r=0;
        fail[1]=0;
        q[r++]=1;
        while(l<r) {
            p=q[l++];
            for(int i=0;i<26;i++) 
                if(a[p][i]) {
                    int k=fail[p];
                    while(!a[k][i]) k=fail[k];
                    fail[a[p][i]]=a[k][i];    
                    q[r++]=a[p][i];
                }
        }
        for(int i=r-1;i>=0;i--) 
            sum[fail[q[i]]]+=sum[q[i]];
    }
    
    AC() {
        cnt=1;
        for(int i=0;i<26;i++) a[0][i]=1;        
    }
}am;

int pos[maxn];
int n;

int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++) am.insert(pos[i]); 
    am.get_fail();
    for(int i=1;i<=n;i++) printf("%d\n",am.sum[pos[i]]);
    return 0;    
}
posted @ 2016-07-01 01:06  invoid  阅读(103)  评论(0编辑  收藏  举报