美文网首页
前缀树笔记

前缀树笔记

作者: M_lear | 来源:发表于2020-09-30 15:51 被阅读0次

1. 前言

发现笔试题经常爱考前缀树。

每次慢慢回忆,再慢悠悠的写代码,时间根本来不及。

需要总结一下记忆方法,目标是以后碰到前缀树的题能上手就来。

记忆点一:图


前缀树

记忆点二:

  1. 边表示字符,结点对应 isEnd 和 count 信息。
  2. 从结点到根只有一条路径,表示一个字符串前缀。

记忆点三:

class Trie {
    final int max = 1000;
    int[][] tree = new tree[max][26]; // 一行表示一个结点;孩子表示法
    int[] count = new int[max];
    boolean[] isEnd = new boolean[max];
    int last;

    public void insert(String word) {
        int p = 0;
        for(int i = 0; i < word.length(); ++i) {
            int c = word.charAt(i)-'a';
            if(tree[p][c] == 0) tree[p][c] = ++last; // 分配结点
            p = tree[p][c];
            count[p]++; // 更新结点对应的计数信息
        }
        isEnd[p] = true; // 更新 isEnd 信息
    }
}

原来总结知识点的时候,总想总结的很全,没有纰漏。
结果就是总结的很复杂,抓不住重点,华而不实。
大脑抗拒复杂的东西,总结的再全再复杂,只能在纸上,不能进脑子里,都是白搭。

所以别看前缀树那么多操作,记住插入即可!

2. 阿里笔试题

题目描述:
输入法里预存了一些词汇。在我们输入一个单词时,我们有两种操作可选:

  1. 花费 1 个单位的时间输入一个字符。
  2. 在我们输入时,输入法会以你的输入作为前缀去匹配它的预存词汇,并给出匹配的 m 个单词,你可以花费 m 个单位的时间选择任一单词代替现有输入。

问输入一个单词所花费的最短时间是多少。

输入描述:
第一行两个整数 n,p,后面每一行表示一个单词,前 n 行为输入法的预存词汇,后 p 行表示 p 个单词输入。

输出描述:
输出 p 行,表示 p 个单词输入对应的最短时间。

例如:
输入,
3 1
nt
nowcoder
nc
nowcoderhhh

输出,
6

输出解释:输入 no 花费 2 个单位时间,只匹配一个词 nowcoder,花费 1 个单位时间用 nowcoder 替换 no,最后花 3 个单位时间输入后面的 hhh。共花费 6 个单位时间。

代码求解:

import java.util.*;

public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), p = sc.nextInt();
        
        Trie trie = new Trie();
        for(int i = 0; i < n; ++i) {
            trie.insert(sc.next());
        }
        
        for(int i = 0; i < p; ++i) {
            char[] word = sc.next().toCharArray();
            // 首先判断前缀树里是否有 word 的前缀,有的话最长前缀有多长
            int len = trie.longestPrefix(word);
            if(len < 0) {
                System.out.println(word.length);
                continue;
            }
            int min = len+1;
            // 得到最长前缀的最短时间
            for(int j = 0; j <= len; ++j) {
                int temp = j+1+trie.countPrefix(word, j);
                if(temp < min) min = temp;
            }
            System.out.println(min+(word.length-1-len));
        }
    }
}


class Trie{
    final int max = 1000;
    int[][] tree = new int[max][26];
    int[] count = new int[max];
    boolean[] isEnd = new boolean[max];
    int last;
    
    public void insert(String word) {
        int p = 0;
        for(int i = 0; i < word.length(); ++i) {
            int c = word.charAt(i) - 'a';
            if(tree[p][c] == 0) tree[p][c] = ++last;
            p = tree[p][c];
            count[p]++;
        }
        isEnd[p] = true;
    }
    
    public int countPrefix(char[] prefix, int j) {
        int p = 0;
        for(int i = 0; i <= j; ++i) {
            int c = prefix[i] - 'a';
            if(tree[p][c] == 0) return 0;
            p = tree[p][c];
        }
        return count[p];
    }
    
    public int longestPrefix(char[] word) {
        int p = 0;
        for(int i = 0; i < word.length; ++i) {
            int c = word[i] - 'a';
            if(tree[p][c] == 0 && isEnd[p]) return i-1;
            p = tree[p][c];
        }
        return -1;
    }
}

3. 附录:前缀树的更常规实现

class Trie {
    class TrieNode{
        TrieNode[] links = new TrieNode[26];
        boolean isEnd;
    }

    TrieNode root;

    /** Initialize your data structure here. */
    public Trie() {
        root = new TrieNode();
    }
    
    /** Inserts a word into the trie. */
    public void insert(String word) {
        TrieNode p = root;
        for(int i = 0; i < word.length(); ++i){
            char c = word.charAt(i);
            if(p.links[c-'a'] == null) p.links[c-'a'] = new TrieNode();
            p = p.links[c-'a'];
        }
        p.isEnd = true;
    }

    private TrieNode searchPrefix(String prefix){
        TrieNode p = root;
        for(int i = 0; i < prefix.length(); ++i){
            char c = prefix.charAt(i);
            if(p.links[c-'a'] == null) return null;
            p = p.links[c-'a'];
        }
        return p;
    }
    
    /** Returns if the word is in the trie. */
    public boolean search(String word) {
        TrieNode p = searchPrefix(word);
        return p == null ? false : p.isEnd;
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
        TrieNode p = searchPrefix(prefix);
        return p != null;
    }
}

参见 leetcode 题目:https://leetcode-cn.com/problems/implement-trie-prefix-tree/

相关文章

  • 数据结构基础--前缀树&&后缀树

    本文只是自己的笔记,并不具备过多的指导意义。 前缀树 何为前缀树 前缀树又名字典树,单词查找树,Trie树,是一种...

  • 前缀树笔记

    1. 前言 发现笔试题经常爱考前缀树。 每次慢慢回忆,再慢悠悠的写代码,时间根本来不及。 需要总结一下记忆方法,目...

  • 前缀树(字典树/Trie)Java实现和应用

    摘要: 前缀树,字典树,插入查询逻辑,Java实现,时间复杂度分析 前缀树介绍 Trie树又被称为前缀树、字典树,...

  • 算法笔记之前缀树

    前缀树 前缀树(trie)又被称为字典树,是一种树形结构,是哈希树的变种。典型应用是用于统计和排序大量的字符串(但...

  • 前缀树

    前缀树又名Tries树、字典树、单词查找树等,常用于快速检索,大量字符串的排序和统计等。 三个基本性质 根节点不包...

  • 前缀树

    前缀树原理

  • 前缀树

    概念: 简述:又名单词查找树,tries树,一种多路树形结构,常用来操作字符串(但不限于字符串),和hash效率有...

  • 前缀树

    题目 给定一个字符串数组,其中不含有重复字符串,判断是否有字符串是另一个字符串的前缀 思路 实现前缀树即可,判断是...

  • 前缀树

    1.什么是前缀树 前缀树是N叉树的一种特殊形式。通常来说,一个前缀树是用来存储字符串的。前缀树的每一个节点代表一个...

  • 前缀树

    最近看代码,发现了一个敏感词检测是用前缀树写的,看起来速度蛮快,毕竟是拿空间换时间,LOG倍速。但是缺点也很明显,...

网友评论

      本文标题:前缀树笔记

      本文链接:https://www.haomeiwen.com/subject/ckiuuktx.html