trie 树,又叫字典树,前缀树,能够用来求查询这个字符串的数量,或者以这个字符串为前缀的字符串的数量。ios
trie 树用了一个前缀的思想,就是把每一个字符串拆一位位拆开来,而后存在树的边上,好比有 cup,apple,cake,app,blog 这几个单词,那么咱们就能够构建这样一棵有根树:
可是,在树上边是很很差维护的,因而咱们能够把他放在这条边的两个端点中,深度较大的那个点上,像这样:
但这样还没完,咱们如何判断这是一个完整字符串呢?好比,你如何知道 ca 是否是一个完整的字符串呢?是判断最后一个字符是否为叶子节点吗?明显不是,上面的例子中,app的最后一个字符就不是叶子节点。那怎么办呢?直接给节点打个标记嘛~
因而,咱们知道了什么是 trie 树,接下来咱们要解决插入和查询的问题。
咱们能够开一个数组,用 \(trie_{i,j}\) 表示节点 i 的儿子中,是字符 j 的节点的编号。固然,咱们不能用字符作下标,可是咱们能够把他转成数字,好比题目说明只会出现 a~z 的字符,那么咱们能够用 x-'a'
来表示字符 x。
而后,对于插入和查询操做,咱们只要枚举字符串的每一位,而后用一个变量 rt
来记录当前节点的编号。而后,对于第 i 个字符,咱们只要查询它是否存在,也就是 \(trie_{rt,s[i-1]}\) 是否为 \(0\)(字符串下标从 \(0\) 开始),若是为 \(0\),如果插入,则加入这个节点;如果查询,则返回 false。
代码以下:数组
void insert(string s) { int len=s.length(),root=0;//root即上面的rt for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) trie[root][id]=++tot;//tot记录节点数 root=trie[root][id]; } v[root]=true;//v就是用来记录是否为字符串结尾的、 return ; } bool find(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) return false; root=trie[root][id]; } if(v[root]) return true; else return false; }
而后,上面说到咱们能够查询以这个字符串为前缀的字符串有多少,怎么弄呢?咱们能够用 \(sum_i\) 表示有从根节点到 \(i\) 这个字符串(trie 树上从根节点到一个节点的路径其实就是一个字符串的前缀)前缀的字符串的数量(有点绕、),而后每次插入走到一个节点就 sum[i]++
便可,而查询则和上面的同样,最后返回末尾节点的 sum 便可。
代码实现以下:app
int fin_pre(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) return 0; root=trie[root][id]; } return sum[root]; }
总体代码实现以下:函数
#include<cstdio> #include<string> #include<iostream> using namespace std; int n,m; int tot,trie[1000005][26],sum[1000005]; bool v[1000005]; void insert(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) trie[root][id]=++tot; root=trie[root][id]; sum[root]++; } v[root]=true; return ; } bool find(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) return false; root=trie[root][id]; } if(v[root]) return true; else return false; } int find_pre(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) return 0; root=trie[root][id]; } return sum[root]; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { string s; cin>>s; insert(s); } for(int i=1;i<=m;i++) { int opt; scanf("%d",&opt);//opt=1表示查询这个字符串是否存在,opt=2表示查询包含这个字符串前缀的字符串的数量 string s; cin>>s; if(opt==1) printf("%s\n",find(s)?"Yes":"No"); else printf("%d\n",find_pre(s)); } return 0; }
因为没有找到合适的模板题因此没法测验,但 insert 和 find 函数是没问题的,find_pre 也不会出太大的问题spa