给你100000个长度不超过10的单词。对于每一个单词,我们要判断他出没出现过,如果出现了,第一次出现第几个位置。
这题当然可以用hash来,但是我要介绍的是trie树。在某些方面它的用途更大。比如说对于某一个单词,我要询问它的前缀是否出现过。这样hash就不好搞了,而用trie还是很简单。
现在回到例子中,如果我们用最傻的方法,对于每一个单词,我们都要去查找它前面的单词中是否有它。那么这个算法的复杂度就是O(n^2)。显然对于100000的范围难以接受。现在我们换个思路想。假设我要查询的单词是abcd,那么在他前面的单词中,以b,c,d,f之类开头的我显然不必考虑。而只要找以a开头的中是否存在abcd就可以了。同样的,在以a开头中的单词中,我们只要考虑以b作为第二个字母的……这样一个树的模型就渐渐清晰了……
我们可以看到,trie树每一层的节点数是26^i级别的。所以为了节省空间。我们用动态链表,或者用数组来模拟动态。空间的花费,不会超过单词数×单词长度。(转自一大牛)
Trie树的java代码 实现如下:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** *//** * A word trie which can only deal with 26 alphabeta letters. * @author Leeclipse * @since 2007-11-21 */ public class Trie{ private Vertex root;//一个Trie树有一个根节点 //内部类 protected class Vertex{//节点类 protected int words; protected int prefixes; protected Vertex[] edges;//每个节点包含26个子节点(类型为自身) Vertex() { words = 0; prefixes = 0; edges = new Vertex[26]; for (int i = 0; i < edges.length; i++) { edges[i] = null; } } } public Trie () { root = new Vertex(); } /** *//** * List all words in the Trie. * * @return */ public List< String> listAllWords() { List< String> words = new ArrayList< String>(); Vertex[] edges = root.edges; for (int i = 0; i < edges.length; i++) { if (edges[i] != null) { String word = "" + (char)('a' + i); depthFirstSearchWords(words, edges[i], word); } } return words; } /** *//** * Depth First Search words in the Trie and add them to the List. * * @param words * @param vertex * @param wordSegment */ private void depthFirstSearchWords(List words, Vertex vertex, String wordSegment) { Vertex[] edges = vertex.edges; boolean hasChildren = false; for (int i = 0; i < edges.length; i++) { if (edges[i] != null) { hasChildren = true; String newWord = wordSegment + (char)('a' + i); depthFirstSearchWords(words, edges[i], newWord); } } if (!hasChildren) { words.add(wordSegment); } } public int countPrefixes(String prefix) { return countPrefixes(root, prefix); } private int countPrefixes(Vertex vertex, String prefixSegment) { if (prefixSegment.length() == 0) { //reach the last character of the word return vertex.prefixes; } char c = prefixSegment.charAt(0); int index = c - 'a'; if (vertex.edges[index] == null) { // the word does NOT exist return 0; } else { return countPrefixes(vertex.edges[index], prefixSegment.substring(1)); } } public int countWords(String word) { return countWords(root, word); } private int countWords(Vertex vertex, String wordSegment) { if (wordSegment.length() == 0) { //reach the last character of the word return vertex.words; } char c = wordSegment.charAt(0); int index = c - 'a'; if (vertex.edges[index] == null) { // the word does NOT exist return 0; } else { return countWords(vertex.edges[index], wordSegment.substring(1)); } } /** *//** * Add a word to the Trie. * * @param word The word to be added. */ public void addWord(String word) { addWord(root, word); } /** *//** * Add the word from the specified vertex. * @param vertex The specified vertex. * @param word The word to be added. */ private void addWord(Vertex vertex, String word) { if (word.length() == 0) { //if all characters of the word has been added vertex.words ++; } else { vertex.prefixes ++; char c = word.charAt(0); c = Character.toLowerCase(c); int index = c - 'a'; if (vertex.edges[index] == null) { //if the edge does NOT exist vertex.edges[index] = new Vertex(); } addWord(vertex.edges[index], word.substring(1)); //go the the next character } } public static void main(String args[]) //Just used for test { Trie trie = new Trie(); trie.addWord("China"); trie.addWord("China"); trie.addWord("China"); trie.addWord("crawl"); trie.addWord("crime"); trie.addWord("ban"); trie.addWord("China"); trie.addWord("english"); trie.addWord("establish"); trie.addWord("eat"); System.out.println(trie.root.prefixes); System.out.println(trie.root.words); List< String> list = trie.listAllWords(); Iterator listiterator = list.listIterator(); while(listiterator.hasNext()) { String s = (String)listiterator.next(); System.out.println(s); } int count = trie.countPrefixes("ch"); int count1=trie.countWords("china"); System.out.println("the count of c prefixes:"+count); System.out.println("the count of china countWords:"+count1); } } 运行: C:\test>java Trie 10 0 ban china crawl crime eat english establish the count of c prefixes:4 the count of china countWords:4
文章来自:http://oivoiv.blog.163.com/
相关推荐
POJ2525-Text Formalization是一个涉及Trie树的经典问题,它教会我们在面对字符串处理时,如何利用Trie树这一高效的数据结构来简化问题。通过理解和实践,不仅可以提升我们的算法能力,还能加深对数据结构应用的洞察...
- **Trie树**:Trie树是一种树形结构,特别适合于字符串的检索,可以用来统计不同查询的频率。 例如,在一个每条记录大小为16字节、总大小为1GB的数据集中,可以先将其切分为100个小文件,每个文件10MB大小。然后,...
4. **Aho-Corasick 算法**:在Trie树的基础上增加了失败指针,一次扫描即可匹配所有敏感词,提高了效率。 二、敏感词库 敏感词库通常包含两类词汇:一类是法律规定的禁止词汇,如涉政、涉黄、涉暴等;另一类是企业...
2. **高级数据结构的应用**:使用高级数据结构解决复杂问题,如平衡树用于维护有序集合,Trie树用于高效字符串查询等。 3. **复杂问题解决**:网络流、最优化问题、编码解码、博弈论、图论中的NP完全问题等。 4. **...
“高级数据结构.rar”可能包含更复杂的数据结构,如平衡二叉树(AVL树、红黑树)、Trie树(字典树)、B树、B+树等,它们在高效存储和检索大量数据时有着重要作用。例如,平衡二叉搜索树保证了插入和查找的效率,而B...
5. Trie树:基本概念和应用 6. 左偏树:可合并堆 7. 双端队列:基本概念和应用 数学部分 1. 组合数学:容斥原理、抽屉原理、置换群与Polya定理 2. 递推关系和母函数 3. 数论:素数问题、概率判素算法、概率因子...
生成树问题 最小生成树 第k小生成树 最优比率生成树 0/1分数规划 度限制生成树 连通性问题 强大的DFS算法 无向图连通性 割点 割边 二连通分支 有向图连通性 强连通分支 2-SAT 最小点基 有向无环...
- **处理策略**:分而治之(hash映射)、数据结构优化(如Bloom filter、Bitmap、Trie树、堆、数据库索引等)、分布式处理(Hadoop、MapReduce)。 3. **Bloom Filter** - **应用**:数据判重、集合交集判断。 -...
将敏感词插入到Trie树中,之后遍历文本,通过Trie树快速判断每个单词是否是敏感词。 7. **模糊匹配**: 有时候,敏感词可能会被部分替换或变形,这时可以使用模糊匹配。例如,使用Levenshtein距离计算文本与敏感词...
2. **数据结构**:链表、数组、栈、队列、树(二叉树、平衡树、堆)、图等基本数据结构的使用,以及高级数据结构如红黑树、B树、Trie树等。 3. **编程语言**:通常以C++为主,涉及面向对象编程、模板类、STL(标准...
3. **Trie树(字典树)**:敏感词库构建为Trie树结构,可以快速查找是否存在敏感词。插入和查找的时间复杂度都是O(m),m为敏感词长度,适合大量敏感词的情况。 4. **Aho-Corasick算法**:基于Trie树的优化,可以在...
后缀树是一种高效的数据结构,用于处理字符串的模式匹配问题。它能快速查找字符串的所有子串,常用于生物信息学中的DNA序列分析和文本索引。 六、Patricia Trie(PAT树) PAT树是优化后的Trie结构,减少了节点数量...
这些题目覆盖了海量数据处理的关键技术,包括数据结构(如Trie树、Bloom Filter、Bitset)、排序算法(如基数排序、快速排序)、分布式计算框架(如MapReduce)、空间效率优化(如Top-K、布隆过滤器)等。...
首先,使用Trie树存储字典中的所有单词,这样可以在O(L)的时间复杂度内搜索任意长度为L的单词。其次,对于每个插入的单词,构建一个Hash表来记录每个单词字符的频率分布,这有助于快速识别兄弟单词。 **查询流程:*...
1. 广搜的状态优化:利用 M 进制数存储状态、转化为串用 hash 表判重、按位压缩存。 2. 深搜的优化:尽量用位运算、一定要加剪枝、函数参数尽可能少、层数不易过大。 八、动态规划 1. 需要用数据结构优化的动态...
10. 素数问题、概率判素算法、概率因子分解 数据结构 1. 二叉堆 2. 左偏树 3. 二项树 4. 胜者树 5. 跳跃表 6. 样式图 7. 斜堆 8. Hash 表 9. 并查集 10. 路径压缩思想的应用 STL 中的数据结构 1. vector 2. ...
在编程实践中,我们可以采用动态规划、贪心、分治等算法思想,结合数据结构如哈希表、Trie树、字典树等来提高效率。此外,利用已有的自然语言处理库,如NLTK、spaCy等,也可以简化某些步骤的实现。 总的来说,POJ...
Trie树 二叉查找树 线段树 RMQ LCA+RMQ SB-Tree 数论 生成紧凑素数表 分解质因子 最大公约数 a^b mod n 扩张欧几里德算法 素数表质因子分解 Stirling公式 中国剩余定理 欧拉数(递推法) 欧拉数(公式...
6. **数据结构**:如堆(优先队列)、平衡二叉搜索树(AVL、红黑树)、字典树(Trie)等,用于高效地进行查找、插入和删除操作。 7. **模拟**:对于某些问题,可能需要编写程序模拟特定过程,如游戏策略、物理过程...