题目

3172: [Tjoi2013]单词

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 3655  Solved: 1757
[Submit][Status][Discuss]

Description

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

Input

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

Output

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

Sample Input

3
a
aa
aaa

Sample Output

6
3
1

HINT

 
 

题解

首先很自然地想到暴力。。。。

直接进行多串匹配
可惜会TLE

附上代码:

 

//TLE代码..........
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

struct Node
{
    Node *fail;
    Node *next[27];
    int id;
    Node()
    {
        fail = NULL;
        id = 0;
        memset(next,NULL,sizeof(next));
    }
};
char T[205][1000005];
int _cnt[1005];
int l[1005];
int belong[1005];
void buildTrie(char *str,Node *root,int id)
{
    Node *p = root,*q;
    int len=l[id];
    for(int i=0; i<len; i++)
    {
        int idx = str[i]-'a';
        if(p->next[idx]==NULL) p->next[idx]=new Node();
        p = p->next[idx];
    }
    if(p->id==0)
        p->id = id;
    belong[id]=p->id;
}
void build_AC_automation(Node *root)
{
    Node* q[200005];
    int pi=1,h=0;
    root->fail = NULL;
    q[++h]=root;
    while(pi<=h)
    {
        Node *p = NULL;
        Node *temp = q[pi];
        pi++;
        for(int i=0; i<27; i++)
        {
            if(temp->next[i]!=NULL)
            {
                if(temp==root) temp->next[i]->fail=root;
                else
                {
                    p=temp->fail;
                    while(p!=NULL)
                    {
                        if(p->next[i]!=NULL)
                        {
                            temp->next[i]->fail=p->next[i];
                            break;
                        }
                        p=p->fail;
                    }
                    if(p==NULL) temp->next[i]->fail=root;
                }
                q[++h]=temp->next[i];
            }
        }
    }
    
}
void query(char *str,int li,Node *root)
{
    Node *p = root;
    int len=li;
    for(int i=0; i<len; i++)
    {
        int idx = str[i]-'a';
        while(p->next[idx]==NULL&&p!=root) p = p->fail;
        p = p->next[idx];
        p = (p==NULL)?root:p;
        Node * temp = p;
        while(temp!=root)
        {
            if(temp->id!=0)
                _cnt[temp->id]++;
            temp = temp->fail;
        }
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    Node *root = new Node();
    for(int i=1; i<=n; i++)
    {
        scanf("%s",T[i]);
        l[i]=strlen(T[i]);
        buildTrie(T[i],root,i);
    }
    build_AC_automation(root);
    for(int i=1;i<=n;i++) 
        query(T[i],l[i],root);
    for(int i=1; i<=n; i++)
        printf("%d\n",_cnt[belong[i]]);
    return 0;
}

 

 

其实认真的想一下,很容易想到,根据fail指针的定义,

假设有一个字符串A的的末尾为p,那么若p->fail是另一个单词B的末尾

那么字符串A必定包含单词B

so  对于每一个fail指针不为root且NULL的点p(其实root可以不管它)

p->fail->cnt+=p->cnt;

这就判断了全部后缀的情况

当然还要加上建立AC自动机时

经过每个单词末尾的次数。

附上AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

struct Node
{
    Node *fail;
    Node *next[27];
    int cnt;
    Node()
    {
        fail = NULL;
        cnt=0;
        memset(next,NULL,sizeof(next));
    }
};
Node *w[205];
char T[205][1000005];
int l[1005];
int belong[1005];
void buildTrie(char *str,Node *root,int id)
{
    Node *p = root,*q;
    int len=l[id];
    for(int i=0; i<len; i++)
    {
        int idx = str[i]-'a';
        if(p->next[idx]==NULL) p->next[idx]=new Node();
        p = p->next[idx];
        p->cnt++;
    }
    w[id]=p;
}
void build_AC_automation(Node *root)
{
    Node* q[200005];
    int pi=1,h=0;
    root->fail = NULL;
    q[++h]=root;
    while(pi<=h)
    {
        Node *p = NULL;
        Node *temp = q[pi];
        pi++;
        for(int i=0; i<27; i++)
        {
            if(temp->next[i]!=NULL)
            {
                if(temp==root) temp->next[i]->fail=root;
                else
                {
                    p=temp->fail;
                    while(p!=NULL)
                    {
                        if(p->next[i]!=NULL)
                        {
                            temp->next[i]->fail=p->next[i];
                            break;
                        }
                        p=p->fail;
                    }
                    if(p==NULL) temp->next[i]->fail=root;
                }
                q[++h]=temp->next[i];
            }
        }
    }
    for(int i=h;i>=1;i--)
        if(q[i]->fail!=root&&q[i]->fail!=NULL)
            q[i]->fail->cnt+=q[i]->cnt;  
}
int main()
{
    int n;
    scanf("%d",&n);
    Node *root = new Node();
    for(int i=1;i<=n;i++)
    {
        scanf("%s",T[i]);
        l[i]=strlen(T[i]);
        buildTrie(T[i],root,i);
    }
    build_AC_automation(root);
    for(int i=1; i<=n; i++)
        printf("%d\n",w[i]->cnt);
    return 0;
}

 

posted on 2017-03-18 15:12  void_zxh  阅读(126)  评论(0编辑  收藏  举报