[字典树,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\n",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 **/

 

posted @ 2019-11-08 21:10  Railgun000  阅读(211)  评论(0编辑  收藏  举报