暑假集训Day 7 马大嘴的废话(trie树)

题目大意

MZH爱说废话,喜爱“水群”,经常被“提走”,管理员对MZH在群里说话进行了限制:

1、只能说小写英文字母。

2、长度不超过20。

即使这样,也不能阻止MZH“水群”,他在限制的条件下也说了成千上万条废话信息,现在已知MZH说过的N条废话信息,接下来MZH要说M条废话信息,请你回答:对于他说的每条废话信息在已知N条废话信息中出现的次数和(如果一条中出现多次,只算一次)。

比如:MZH说过了3条信息
1.adbdb

2.bcde

3.cdbf

4.db

现在说的信息为db,那么信息db在信息1和信息3和信息4出现过,所以答案为3.

输入格式

第一行,一个整数N。

接下来N行,每行一个字符串(只能由小写字母组成),代表MZH说过的N条废话信息。

再接下来一行,一个整数M。

接下来M行,每行一个字符串(只能由小写字母组成),代表当前MZH要说的一条废话信息。

输出格式

共M行,每行输出该废话信息出现的次数和。

样例

样例输入

20
ad
ae
af
ag
ah
ai
aj
ak
al
ads
add
ade
adf
adg
adh
adi
adj
adk
adl
aes
5
b
a
d
ad
s

输出样例

0
20
11
11
2

数据范围与提示

20%的数据:n<=100 , m<=50

60%的数据:n<=10000, m<=500

100%的数据:n<=10000, m<=100000

算法分析

  • n为10000 m为100000的数据显然暴力枚举每一个字符串是过不去的
  • 这里就要用到我们的trie树(字典树,前缀树)了
  • 首先我们用输入的n个字符串建树 建树方法在trie专题中有 这里就不再赘述
  • 唯一需要注意的点就是判重 在下面的代码中注释有体现
  • 注释挺全面的看注释吧(其实我真的不是懒

代码展示

#include<bits/stdc++.h>
using namespace std;
const int maxn = (1e4+10)*50;
int tree[maxn][26],v[maxn][26],cnt[maxn][26],len,tot;
char s[26];

void add(int l, int r, int id){
	int rt = 0,t;
	while(l < r){//因为要看是否存在于字符串中而不是 是否为前缀 所以要把一个字符串以每个字符为起点存一次
		t = s[l] - 'a';//将字母转化为对应的下标
		if (tree[rt][t]) {//如果当前的字母已经建过节点
			if (v[rt][t] != id) {
				v[rt][t] = id;
				++cnt[rt][t];//当前字符串++
			}
		}
		else {//如果还没有建过当前节点
			tree[rt][t] = ++tot;//新建一个点
			v[rt][t] = id;
			cnt[rt][t]++;
		}
		rt = tree[rt][t];
		++l;
	}
}

int search(){
	int rt = 0,ans = 0;
	for(int i = 0;i < len;++i){
		if(!tree[rt][s[i]-'a'])return 0;//如果当前字符未出现过
		ans = cnt[rt][s[i]-'a'];//即为当前字符串出现的次数
		rt = tree[rt][s[i]-'a'];//为下一次转移做准备
	}
	return ans;
}

int main(){
	int n;scanf("%d",&n);
	for(int i = 0;i < n;++i){
		scanf("%s",s);
		len = strlen(s);
		for(int j = 0;j < len;++j){
			add(j,len,i);
		}
	}
	int m;scanf("%d",&m);
	while(m--){
		scanf("%s",s);
		len = strlen(s);
		printf("%d
",search());
	}
}
如初见 与初见
原文地址:https://www.cnblogs.com/HISKrrr/p/13210485.html