zoukankan      html  css  js  c++  java
  • 单词

    C. 单词

    题目描述

    原题来自:TJOI 2013

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

    输入格式

    第一个一个整数 NNN,表示有多少个单词,接下来 NNN 行每行一个单词。

    输出格式

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

    样例

    样例输入

    3
    a
    aa
    aaa

    样例输出

    6
    3
    1

    数据范围与提示

    对于全部数据,1≤N≤2001le Nle 2001N200,所有单词长度的和不超过 10610^6106​​,保证每个单词由小写字母组成。

    【题解】

      本来以为是个板子题,然后发现并不是。开始居然没有理解题目……文章就是所有的单词合起来,中间加空格,但是空格很碍事,可以一个单词一个单词地匹配。

      这个题让我重新思考了一下AC自动机……一开始我是通过ask操作通过调fail指针来更新count,然后T90,有一个比较恶心的点是每个字母的fail指针都是他的父亲节点,然后就死了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    struct trie
    {
        int count;
        trie *next[26],*fail;
        trie()
        {
            count=0;
            fail=NULL;
            for(int i=0;i<26;i++) next[i]=NULL;
        }
    }*q[10000000],*root;
    int head,tail;
    trie *loc[210];
    int n;
    char keyword[210][1000000];
    
    void insert(char s[],trie *root,int t)
    {
        trie *p=root;
        int i=0,index;
        while(s[i])
        {
            index=s[i]-'a';
            if(p->next[index]==NULL)p->next[index]=new trie();
            p=p->next[index];
            i++;
        }
        loc[t]=p;
    }
    void build_ac(trie *root)
    {
        q[++tail]=root;
        while(head!=tail)
        {
            trie *p=q[++head];
            trie *temp=NULL;
            for(int i=0;i<26;i++)
            if(p->next[i]!=NULL)
            {
                if(p==root)p->next[i]->fail=root;
                else
                {
                    temp=p->fail;
                    while(temp!=NULL)
                    {
                        if(temp->next[i]!=NULL)
                        {
                            p->next[i]->fail=temp->next[i];
                            break;
                        }
                        temp=temp->fail;
                    }
                    if(temp==NULL)p->next[i]->fail=root;
                }
                q[++tail]=p->next[i];
            }
        }
    }
    void ask(trie *root,char str[])
    {
        int i=0,index,cnt=0;
        trie *p=root;
        while(str[i])
        {
            index=str[i]-'a';
            while(p!=root && p->next[index]==NULL)p=p->fail;
            p=p->next[index];
            if(p==NULL)p=root;
            trie *temp=p;
            while(temp!=root)
            {
                temp->count++;
                temp=temp->fail;
            }
            i++;
        }
    }
    signed main()
    {
    //    freopen("in.txt","r",stdin);
        
        scanf("%d",&n);
        trie *root=new trie();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",keyword[i]);
            insert(keyword[i],root,i);
        }
        build_ac(root);
        for(int i=1;i<=n;i++)
            ask(root,keyword[i]);
        for(int i=1;i<=n;i++)
            printf("%d
    ",loc[i]->count);
    }
    错解

      其实可以在插入一个单词的时候对于他的路径上的点的count+1,表示这个前缀又出现了一次,根据AC自动机的性质,可以从下往上把i节点的count加到i->fail去。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    struct trie
    {
        int count;
        trie *next[26],*fail;
        trie()
        {
            count=0;
            fail=NULL;
            for(int i=0;i<26;i++) next[i]=NULL;
        }
    }*q[10000000],*root;
    int head,tail;
    trie *loc[210];
    int n;
    char keyword[210][1000000];
    
    void insert(char s[],trie *root,int t)
    {
        trie *p=root;
        int i=0,index;
        while(s[i])
        {
            p->count++;
            index=s[i]-'a';
            if(p->next[index]==NULL){p->next[index]=new trie();}
            p=p->next[index];
            i++;
        }
        p->count++;
        loc[t]=p;
    }
    void build_ac(trie *root)
    {
        q[++tail]=root;
        while(head!=tail)
        {
            trie *p=q[++head];
            trie *temp=NULL;
            for(int i=0;i<26;i++)
            if(p->next[i]!=NULL)
            {
                if(p==root)p->next[i]->fail=root;
                else
                {
                    temp=p->fail;
                    while(temp!=NULL)
                    {
                        if(temp->next[i]!=NULL)
                        {
                            p->next[i]->fail=temp->next[i];
                            break;
                        }
                        temp=temp->fail;
                    }
                    if(temp==NULL)p->next[i]->fail=root;
                }
                q[++tail]=p->next[i];
            }
        }
    }
    signed main()
    {
    //    freopen("word10.in","r",stdin);
        
        scanf("%d",&n);
        trie *root=new trie();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",keyword[i]);
            insert(keyword[i],root,i);
        }
        build_ac(root);
        for(int i=tail;i>0;i--)
        {
            trie *temp=q[i];
            if(temp->fail!=NULL)temp->fail->count+=temp->count;
        }
        for(int i=1;i<=n;i++)
            printf("%d
    ",loc[i]->count);
    }
    标程
    波澜前,面不惊。
  • 相关阅读:
    python抢票开发——设备预约助手实现
    树莓派的基本网络配置
    python 端口扫描程序
    数据通讯与网络 第五版第24章 传输层协议-TCP协议部分要点
    数据通讯与网络 第五版第24章 传输层协议-UDP协议部分要点
    利用python开发的flappy bird 游戏
    EMACS 快捷键笔记
    python程序中用类变量代替global 定义全局变量
    在树莓派下对多个串口转USB设备进行设备名称绑定操作
    python 编写的经纬度坐标转换类
  • 原文地址:https://www.cnblogs.com/Al-Ca/p/11021713.html
Copyright © 2011-2022 走看看