[字典树,trie树] 树之呼吸-肆之型-前缀统计
D.树之呼吸-肆之型-前缀统计 | |||||
|
|||||
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 **/