020(【模板】字典树)(字典树)
题目:https://www.luogu.com.cn/problem/P8306
题目思路:首先,从它的题目就可以看出来是用字典树做
先把前面一揽子字符串存起来再说
他要求问,给定的这个字符串是多少个前面的一揽子字符串的前缀
还记得字典树的特性吗?它那公共前缀被扔在一起的特性
由此联想,我们可以给每一个节点编上号码,“这个节点和它的那些顶头上司是几个字符串的前缀啊”
跟着这个思路,只需要再把后面那个给定的字符串搜一遍,它最后停在哪个节点,哪个节点所对应的号码就是它所对应的答案
怎么找?硬找。
每一个字符串只要经过这个节点,这个节点所对应的号码就加1
最开始把它们全部设置成0,不就成了。
再提一个醒,本题的字符在字典上并不连续
老老实实用ASCII码的属于徒增烦恼
用一个map即可,map,说白了就是一个谁都可以当下标的数组,就像一根有油的记号笔,目前多以一维的形式存在
#include
using namespace std;
int n,m1,m2,tot=0,id1=0;//id1最开始是0
int trie[3000001][63],code[3000001];
//后面那个63是因为一共有62种字符
string s;
map<char,int>id;//ASCII码可以滚蛋了
void putin(string s){//放进去
int s0=s.size(),p=0;
for(int l=0;lif(!trie[p][id[s[l]]]){
trie[p][id[s[l]]]=++tot;
}
p=trie[p][id[s[l]]];
//上面都是字典树的基础
//不知道的去看我的018博客
code[p]++;
//碰上了就代表“又多一个有联系的”
//加上去
}
}
int finding(string s){
//寻找
int s0=s.size(),p=0;
for(int l=0;lif(!trie[p][id[s[l]]]){
return 0;
//断档了
//意味着没有字符串有像他一样的前缀
//Salut
}
p=trie[p][id[s[l]]];
//碰上了就接着往下走
}
return code[p];
//到地方,下车
}
int main(){
scanf("%d",&n);
//编编编编号
for(char i='a';i<='z';i++){
id[i]=++id1;
}
for(char i='A';i<='Z';i++){
id[i]=++id1;
}
for(char i='0';i<='9';i++){
id[i]=++id1;
}
//编编编编号
while(n--){
for(int i=0;i<=tot;i++){
code[i]=0;
for(int j=0;j<=63;j++){
trie[i][j]=0;
}
}
tot=0;
//别忘了“全部设置成0”
scanf("%d%d",&m1,&m2);
for(int i=1;i<=m1;++i){
cin>>s;
putin(s);
//先把前面一揽子字符串打进去
}
for(int i=1;i<=m2;++i){
cin>>s;
cout<endl;
//找,完了出结果
}
}
return 0;
}
题目总结:再说一说我做题时候的血泪史
第一个,明明设置了一个for(l)然后转头来个 s[i]
第二个,在用tot之前把tot归零
第三个,(!(1)==0),令人无地自容的命令