• P3966 [TJOI2013]单词


    P3966 [TJOI2013]单词

    题目描述

    小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。

    输入输出格式

    输入格式:

    第一行一个整数N,表示有N个单词。接下来N行每行一个单词,每个单词都由小写字母(a-z)组成。(N≤200)

    输出格式:

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

    输入输出样例

    输入样例#1: 
    3
    a
    aa
    aaa
    输出样例#1: 
    6
    3
    1
    

    说明

    数据范围

    30%的数据, 单词总长度不超过10^3

    100%的数据,单词总长度不超过10^6

    看上去题目貌似很简单,不就是把trie树一建然后对每个单词跑一遍AC自动机吗?然而这样会被卡死,考虑如何来优化,由于题目中说每一个单词会多次出现,那么我们就可以统计一下每个单词出现的次数,把这个出现次数看做这个单词的权值,然后在跑AC自动机时直接把这个权值加入到答案中,那么我们如何处理重复出现的单词并统计次数呢?其实我们在trie树上就可以搞定,如果两个单词相同,那么他们在trie树上的结束节点一定相同,这样就可以进行统计了。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cmath>
      4 #include<string>
      5 #include<cstring>
      6 #include<map>
      7 #include<queue>
      8 #include<stack>
      9 #include<algorithm>
     10 #include<vector>
     11 #define maxn 1050005
     12 using namespace std;
     13 
     14 inline int read()
     15 {
     16     char c=getchar();
     17     int res=0,x=1;
     18     while(c<'0'||c>'9')
     19     {
     20         if(c=='-')
     21         x=-1;
     22         c=getchar();
     23     }
     24     while(c>='0'&&c<='9')
     25     {
     26         res=res*10+(c-'0');
     27         c=getchar();
     28     }
     29     return res*x;
     30 }
     31 
     32 int n,tot=1;
     33 int tree[maxn][30],nt[maxn],bo[maxn],ed[maxn],f[maxn],t[maxn];
     34 char a[205][maxn];
     35 queue<int>q;
     36 
     37 void trie(char *s,int num)
     38 {
     39     int len=strlen(s),u=1;
     40     for(register int i=0;i<len;i++)
     41     {
     42         int c=s[i]-'a';
     43         if(!tree[u][c])
     44         {
     45             tree[u][c]=++tot;
     46         }
     47         u=tree[u][c];
     48     }
     49     if(bo[u])
     50     t[num]=1;//t数组表示这个单词之前是否出现过 
     51     bo[u]++;
     52     ed[num]=u;
     53 }
     54 
     55 void bfs()
     56 {
     57     for(register int i=0;i<26;i++)
     58     {
     59         tree[0][i]=1;
     60     }
     61     nt[1]=0;q.push(1);
     62     while(q.size())
     63     {
     64         int u=q.front();q.pop();
     65         for(register int i=0;i<26;i++)
     66         {
     67             if(!tree[u][i])
     68             tree[u][i]=tree[nt[u]][i];
     69             else
     70             {
     71                 int v=tree[u][i];
     72                 q.push(v);
     73                 nt[v]=tree[nt[u]][i];
     74             }
     75         }
     76     }
     77 }
     78 
     79 void find(char *s,int num)
     80 {
     81     int len=strlen(s),u=1,k,ans=bo[ed[num]];//ans表示这个单词出现的次数 
     82     for(register int i=0;i<len;i++)
     83     {
     84         int c=s[i]-'a';
     85         k=tree[u][c];
     86         while(k>1)
     87         {
     88             if(bo[k])
     89             f[k]+=ans;
     90             k=nt[k];
     91         }
     92         u=tree[u][c];
     93     }
     94 }
     95 
     96 int main()
     97 {
     98     n=read();
     99     for(register int i=1;i<=n;i++)
    100     {
    101         scanf("%s",a[i]);
    102         trie(a[i],i);
    103     }
    104     bfs();
    105     for(register int i=1;i<=n;i++)
    106     {
    107         if(!t[i])//这里我们只需要将每个不重复出现的单词跑一边AC自动机 
    108         find(a[i],i);
    109     }
    110     for(register int i=1;i<=n;i++)
    111     {
    112         printf("%d
    ",f[ed[i]]);
    113     }
    114     return 0;
    115 }
    View Code
  • 相关阅读:
    河南省第十届ACM省赛G:Plumbing the depth of lake
    南洋理工oj 题目92 图像有用区域
    初学欧拉图,知识总结,后续增加
    初学并查集知识总结后续增加
    南阳oj 题目42 一笔画问题
    南阳oj 题目 90 整数划分
    南阳oj题目20吝啬的国度 菜鸟的进阶之路
    南阳oj 题目21 三个水杯
    UVA-540 Team Queue
    HDU-1596 find the safest road
  • 原文地址:https://www.cnblogs.com/snowy2002/p/10690646.html
Copyright © 2020-2023  润新知