• [字典树,trie树] 树之呼吸-肆之型-前缀统计


    D.树之呼吸-肆之型-前缀统计
    Time Limit: 1000 MS Memory Limit: 65536 K
    Total Submit: 59 (8 users) Total Accepted: 7 (7 users) Special Judge: No
    Description

    给定 n 个字符串 S1,S2,...,Sn。

    接下来进行 m 次询问,每次询问给定一个字符串 T,

    求 S1 ~ Sn 中有多少个字符串是 T 的前缀。

    Input

    输入第一行为一个正整数 case,表示测试数据组数;

    对于每组测试数据,输入第一行为一个正整数 n,表示给定的字符串数;

    接下来 n 行,给出 n 个字符串 S1 ~ Sn;

    接下来一行一个正整数 m,表示询问数;

    接下来 m 行,给出 m 个询问串 T1 ~ Tm;

    保证:

    case <= 50;

    给定串和询问串均只由大小写字母组成;

    给定串和询问串的总长均不超过 2e5。

    Output
    对于每组测试数据,输出 m 行,每行一个整数,表示有多少个给定串是 T1 ~ Tm 的前缀。
    Sample Input

    2

    3

    aab

    aa

    a

    3

    aa

    aab

    aabc

    3

    ABA

    ABA

    AAB

    3

    AB

    ABAab

    AAB

    Sample Output

    2

    3

    3

    0

    2

    1

    Author
    陈鑫

    题意: 给定 n 个字符串 S1,S2,...,Sn.
    接下来进行 m 次询问,每次询问给定一个字符串 T,
    求 S1 ~ Sn 中有多少个字符串是 T 的前缀.
    思路:给n个单词建一个字典树,在枚举Ti的前缀,看字典树中是否存在这个前缀,如果存在则答案加上这个单词出现的次数
    注意:
    这里正常来说按题意数组长度应该开2e5,但用memset会超时,正确做法是用fill并且去掉第一次初始化(把初始化放结尾),这里开1e5能过是因为数据水了,
    注意字母有大小写A在a的前面,所以s[i]-'A',来得到字母的编号
    ed[rt]标记不仅可以表示是否存在,也可以用来统计出现的次数
    注意如果用了freopen则不能关闭流同步,也就是ios::sync_with_stdio(0),否则可以关闭流同步

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 using namespace std;
     5 const int amn=1e5+1;    ///这里正常来说应该开2e5,但用memset会超时,正确做法是用fill并且去掉第一次初始化(把初始化放结尾),这里开1e5能过是因为数据水了,
     6 int tr[amn][60],ed[amn],tot,x,ans,len,rt;
     7 void init(){
     8     memset(tr,0,sizeof tr); ///在2e5的情况下的正确做法是用fill,不然用memset会超时
     9     memset(ed,0,sizeof ed);
    10     tot=0;
    11 }
    12 char s[amn];
    13 void add(){
    14     len=strlen(s),rt=0;
    15     for(int i=0;i<len;i++){
    16         x=s[i]-'A';         ///注意字母有大小写A在a的前面,所以s[i]-'A',来得到字母的编号
    17         if(!tr[rt][x])tr[rt][x]=++tot;
    18         rt=tr[rt][x];
    19     }
    20     ed[rt]++;   ///这里由ed[rt]=1,变为了ed[rt]++,可以统计相同的单词出现了几次
    21 }
    22 void sol(){
    23     len=strlen(s),rt=0,ans=0;
    24     for(int i=0;i<len;i++){
    25         x=s[i]-'A';
    26         if(!tr[rt][x])return;   ///如果下面没结点就不用匹配了
    27         rt=tr[rt][x];
    28         if(ed[rt])ans+=ed[rt];  ///如果出现了一个单词,则答案加上单词出现的次数
    29     }
    30 }
    31 int main(){
    32 //    freopen("in.txt","r",stdin);
    33 //    cin.sync_with_stdio(0);
    34 ///注意如果用了freopen则不能关闭流同步,也就是ios::sync_with_stdio(0),否则可以关闭流同步
    35     int T,n,m;scanf("%d",&T);
    36     while(T--){
    37         scanf("%d",&n);
    38         init();
    39         while(n--){
    40             cin>>s;
    41             add();
    42         }
    43         scanf("%d",&m);
    44         while(m--){
    45             cin>>s;
    46             sol();
    47             printf("%d
    ",ans);
    48         }
    49     }
    50 }
    51 /**
    52 题意: 给定 n 个字符串 S1,S2,...,Sn.
    53 接下来进行 m 次询问,每次询问给定一个字符串 T,
    54 求 S1 ~ Sn 中有多少个字符串是 T 的前缀.
    55 思路:给n个单词建一个字典树,在枚举Ti的前缀,看字典树中是否存在这个前缀,如果存在则答案加上这个单词出现的次数
    56 注意:
    57 这里正常来说按题意数组长度应该开2e5,但用memset会超时,正确做法是用fill并且去掉第一次初始化(把初始化放结尾),这里开1e5能过是因为数据水了,
    58 注意字母有大小写A在a的前面,所以s[i]-'A',来得到字母的编号
    59 ed[rt]标记不仅可以表示是否存在,也可以用来统计出现的次数
    60 注意如果用了freopen则不能关闭流同步,也就是ios::sync_with_stdio(0),否则可以关闭流同步
    61 **/
  • 相关阅读:
    【SPOJ 104】Highways
    Test_Codes
    【关路灯】【MM不哭】
    省选悲剧
    【HNOI 2002】营业额统计
    博弈论 Nimm Games
    【NOI 2008】志愿者招募 Employee
    【APIO2009】ATM
    【SDOI 2009】学校食堂 Dining
    POJ 1001
  • 原文地址:https://www.cnblogs.com/Railgun000/p/11823303.html
Copyright © 2020-2023  润新知