单词

C. 单词

题目描述

原题来自:TJOI 2013

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

输入格式

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

输出格式

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

样例

样例输入

3
a
aa
aaa

样例输出

6
3
1

数据范围与提示

对于全部数据,1≤N≤2001\le N\le 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\n",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\n",loc[i]->count);
}
标程
posted @ 2019-06-14 09:37  Al_Ca  阅读(180)  评论(0编辑  收藏  举报
ヾ(≧O≦)〃嗷~