AC自动机谜题

兔子们在玩字符串的游戏。首先,它们拿出了一个字符串集合 S,然后它们定义一个字符串为“好”的,当且仅当它可以被分成非空的两段,其中每一段都是字符串集合 S 中某个字符串的前缀。
比如对于字符串集合{"abc", "bca"},字符串"abb","abab"是“好”的("abb" = "ab"+"b", abab = "ab" + "ab"),而字符串“bc”不是“好”的。
兔子们想知道,一共有多少不同的“好”的字符串。

输入格式:

第一行一个整数 n,表示字符串集合中字符串的个数
接下来每行一个字符串

输出格式:

一个整数,表示有多少不同的“好”的字符串

样例输入:

2
ab
ac

样例输出:

9

数据范围:

对于 20%的数据,1 <= n <= 200
对于 50%的数据,1 <= n <= 2000
对于 100%的数据,1 <= n <= 10000,每个字符串非空且长度不超过 30,均为小写字母组成。

时间限制:

3s

空间限制:

512m
 
这是一道AC自动机的题.
我订题时,发现我的写法与大家的不一样.
别人是减去fail树上子树大小减一.
我是另一种写法.
对每个前缀找后缀相等的,长度比他短的另一前缀是否存在(即fail)
存在的话,ans-前缀集合中后缀为上文两前缀的前缀个数.
这竟然和fail子树大小等价!!!
玄学! 疑问尚未解决.
#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
char a[1000010],s[1000010];
long long ans;
int sz,n;
int ch[1000010][26],f[1000010],v[1000010],val[1000010],fa[1000010],dep[1000100],tag[1000010];
void init(){
    sz=0; ans=0;
    memset(ch[0],0,sizeof(ch[0]));
    memset(f,0,sizeof(f));
    memset(v,0,sizeof(v));
}
void insert(char *t,int p){
    int x=0;
    for (int i=0; t[i]; i++){
        int y=t[i]-'a';
        if (!ch[x][y]){
            ch[x][y]=++sz;
            dep[sz]=dep[x]+1;
            fa[sz]=x;
            memset(ch[sz],0,sizeof(ch[sz]));
        }
        x=ch[x][y];
    }
}
void getfail(){
    queue<int> q;    
    f[0]=0;
    for (int i=0; i<=25; i++) if (ch[0][i]) q.push(ch[0][i]);
    while (!q.empty()){
        int x=q.front();
        q.pop();
        for (int i=0; i<=25; i++){
            int    *y=&ch[x][i],*z=&ch[f[x]][i];
            if (!(*y)){
                (*y)=(*z);
                continue;
            }
            q.push(*y);
            f[*y]=(*z);
        }
    }
}
void re(int orz){
    int x=orz;
    while (f[x]){
        x=f[x];
        int f=orz,t=dep[x];
        while (t--) f=fa[f];
        ++tag[f];
        break;//?
    }
}
void calc(int orz){
    int x=orz;
    while (f[x]){
        x=f[x];
        ans-=tag[x];
    }
}
int main(){
    scanf("%d",&n);
    init();
    for (int i=1; i<=n; i++){
        scanf("%s",a);
        insert(a,i);
    }
    ans=1ll*sz*sz;
    getfail();
//    for (int i=1; i<=sz; ++i) cerr<<last[i]<<" "; cerr<<endl;
//    for (int i=1; i<=sz; ++i) cerr<<dep[i]<<" "; cerr<<endl;
    for (int i=1; i<=sz; ++i) re(i);
//    for (int i=1; i<=sz; ++i) cerr<<tag[i]<<" "; cerr<<endl;
//    for (int i=1; i<=sz; ++i) cerr<<val[i]<<" "; cerr<<endl;
    for (int i=1; i<=sz; ++i) calc(i);
    printf("%lld",ans);
    return 0;
}

有待思考.

posted @ 2018-03-16 21:31  Yuhuger  阅读(343)  评论(0编辑  收藏  举报