• BZOJ_3172_[Tjoi2013]单词_后缀自动机


    BZOJ_3172_[Tjoi2013]单词_后缀自动机

    Description

    某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。

    Input

    第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6

    Output

    输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。

    Sample Input

    3
    a
    aa
    aaa

    Sample Output

    6
    3
    1

    用AC自动机的话应该会比较好写吧。https://www.cnblogs.com/suika/p/9128027.html
    这里尝试了下后缀自动机。相当于查询某个子串出现了多少次。
    两种写法,建立广义后缀自动机或者中间用'{'分隔开。
    我比较喜欢前者,好写且方便。
    方法1:插入时标记每个串最后出现的节点位置,然后DP即可。
    代码:
    #include <cstdio>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    #define N 1000050
    int ch[N<<1][26],fa[N<<1],dep[N<<1],cnt=1,lst,n,flg[N];
    int ws[N<<1],a[N<<1],siz[N<<1];
    char w[N];
    void insert(int x) {
        int p=lst,np,q,nq;
        if(ch[p][x]) {
            q=ch[p][x];
            if(dep[q]==dep[p]+1) lst=q;
            else {
                fa[nq=++cnt]=fa[q]; lst=nq;
                dep[nq]=dep[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[q]=nq;
                for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
            }
        }else {
            np=++cnt; lst=np; dep[np]=dep[p]+1;
            for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
            if(!p) fa[np]=1;
            else {
                q=ch[p][x];
                if(dep[q]==dep[p]+1) fa[np]=q;
                else {
                    fa[nq=++cnt]=fa[q];
                    dep[nq]=dep[p]+1;
                    memcpy(ch[nq],ch[q],sizeof(ch[q]));
                    fa[q]=fa[np]=nq;
                    for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
                }
            }
        }
    }
    int main() {
        scanf("%d",&n);
        int i,j;
        for(i=1;i<=n;i++) {
            scanf("%s",w+1);
            lst=1;
            for(j=1;w[j];j++) insert(w[j]-'a'),siz[lst]++;
            flg[i]=lst;
        }
        for(i=1;i<=cnt;i++) ws[dep[i]]++;
        for(i=1;i<=cnt;i++) ws[i]+=ws[i-1];
        for(i=cnt;i;i--) a[ws[dep[i]]--]=i;
        for(i=cnt;i;i--) {
            int p=a[i];
            siz[fa[p]]+=siz[p];
        }
        for(i=1;i<=n;i++) {
            printf("%d
    ",siz[flg[i]]);
        }
    }
    

     方法2:注意每个串对应的结点不一定是一开始插入的那个结点。

    需要每次找一遍,比较麻烦。

    代码:

    #include <cstdio>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    #define N 2000050
    int ch[N<<1][27],fa[N<<1],dep[N<<1],cnt=1,lst=1,n,flg[N];
    int ws[N<<1],a[N<<1],siz[N<<1],l[233],r[233];
    char w[N],s[N];
    void insert(int x) {
        int p=lst,np=++cnt,q,nq;
        lst=np; dep[np]=dep[p]+1;
        for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
        if(!p) fa[np]=1;
        else {
            q=ch[p][x];
            if(dep[q]==dep[p]+1) fa[np]=q;
            else {
                fa[nq=++cnt]=fa[q];
                dep[nq]=dep[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[q]=fa[np]=nq;
                for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
            }
        }
    }
    void print() {
        int i,j;
        printf("test-------------------------------------------
    ");
        for(i=1;i<=cnt;i++) {
            printf("p=%d,siz=%d,dep=%d,fa=%d
    ",i,siz[i],dep[i],fa[i]);
            for(j=0;j<=26;j++) {
                if(ch[i][j]) {
                    printf("ch(%d)(%c)=%d
    ",i,j+'a',ch[i][j]);
                }
            }
        }
        printf("lst=%d
    ",lst);
    }
    int main() {
        scanf("%d",&n);
        int i,j,tot=0;
        for(i=1;i<=n;i++) {
            scanf("%s",w+1);
            l[i]=tot+1;
            for(j=1;w[j];j++) s[++tot]=w[j]-'a';
            r[i]=tot;
            s[++tot]=26;
        }
        for(i=1;i<=tot;i++) insert(s[i]),siz[lst]++;
        // printf("%d
    ",flg[2]);
        // print();
        for(i=1;i<=cnt;i++) ws[dep[i]]++;
        for(i=1;i<=cnt;i++) ws[i]+=ws[i-1];
        for(i=1;i<=cnt;i++) a[ws[dep[i]]--]=i;
        for(i=cnt;i;i--) {
            int p=a[i];
            siz[fa[p]]+=siz[p];
        }
        for(i=1;i<=n;i++) {
            int p=1;
            // printf("%d %d
    ",l[i],r[i]);
            for(j=l[i];j<=r[i];j++) p=ch[p][s[j]];
            // printf("p=%d
    ",p);
            printf("%d
    ",siz[p]);
        }
    }
    
    方法
  • 相关阅读:
    【Android编译】各个模块编译方法
    【Android调试遇到的问题总结】手机com口无法识别
    【Android编译error】error: lambda capture 'this' is not used [-Werror,-Wunused-lambda-capture]
    【Open-JDK】配置java的Open-JDK环境
    【Log】java文件中直接加log的方法
    【BT+Log】BT抓取log方法以及log级别修改(HCI+Logcat)
    【Bluetooth】获取蓝牙地址的6种方法,及btnvtool工具使用
    【adb】adb shell 查看 APK 信息(权限等)
    【NFC】NFC Tag和NFC CE(Card Emulation)区别
    【Wifi】adb命令打开/关闭蓝牙、NFC(bluetooth)开关
  • 原文地址:https://www.cnblogs.com/suika/p/9153046.html
Copyright © 2020-2023  润新知