zoukankan      html  css  js  c++  java
  • 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;
    }

    有待思考.

  • 相关阅读:
    背包问题
    计蒜客lev3
    线段树BIT操作总结
    图论题收集
    Codeforces Round #607 (Div. 2) 训练总结及A-F题解
    2-sat 学习笔记
    洛谷 P3338 【ZJOI2014】力/BZOJ 3527 力 题解
    $noi.ac$ #51 array 题解
    洛谷 P3292 【SCOI2016】幸运数字/BZOJ 4568 幸运数字 题解
    洛谷 P5283 【十二省联考2019】异或粽子 题解
  • 原文地址:https://www.cnblogs.com/Yuhuger/p/8585590.html
Copyright © 2011-2022 走看看