• 字典树(前缀树)--java


    前缀树

    从一道较为简单的题来理解


    > **X星球的身份证是一个18位的字符串,每位只包含0~9,上面包含了个人信息。并且根据2个人的身份证可以知道2个人的相似度。相似度:2个人身份证的最长公共前缀的长度。假如A和B的相似度为k,那么A和B的身份证的前面k位相同的,并且第k+1位一定不同。没有两个人的身份证是完全相同的。现在有一个身份证库,保存有n人的身份证帐号。有Q个询问。每个询问给出一个人的身份证,询问身份证库的人和这个人最大的相似度,并且询问身份证库有多少个人和他的相似度等于这个最大相似度。**
    示例输入: (第一行为n,Q,接下来是n个人的身份证号,和Q个查询身份证号)
    3 2
    333333333333333333
    111111122222222222
    111111133333333333
    111111111000000000
    000000000000000000
    

    示例输出:(每个查询Q对应一个最大公共前缀长度(7),以及该身份证库中有多少个人是和该查询有最大公共前缀(2))
    7 2
    0 3
    

    import java.util.Scanner;
    
    //前缀树的树节点
    class TreeNode{
    	public int path;
    	public int end;
    	public TreeNode[] next;
    	
    	public TreeNode(){
    		path = 0;//有多少路径(字符串)经过该节点
    		end = 0;//有多少路径(字符串)以该节点为终点
    		next = new TreeNode[10];//10个字符(0-9)//该节点是否为终点
    	}
    }
    
    
    //前缀树
    class TrieTree{
    	public TreeNode root;
    	public TrieTree(){
    		root = new TreeNode();
    	}
    	
    	public void insert(String str){ //关键在于插入,插入时,在从父节点到子节点的路径上存储一个元素
    		if(str == null || str.length() == 0) {
    			return ;
    		}
    		
    		int len = str.length();
    		TreeNode curNode = root;
    		for(int i=0; i<len; i++){
    			char tmp = str.charAt(i);
    			int index = (int)tmp - '0';
    			if(curNode.next[index] == null){
    				curNode.next[index] = new TreeNode();
    			}
    			curNode.path++;
    			curNode = curNode.next[index];
    		}
    		curNode.path++;
    		curNode.end++;
    	}
    	
    	public int size(){
    		return root.path;
    	}
    	
    	public int count(String str){
    		if(str == null || str.length() == 0){
    			return 0;
    		}
    		int len = str.length();
    		TreeNode curNode = root;
    		for(int i=0; i<len; i++){
    			char tmp = str.charAt(i);
    			int index = (int) tmp - '0';
    			if(curNode.next[index] == null){
    				return 0;
    			}
    			curNode = curNode.next[index];
    		}
    		return curNode.end;
    	}
    	
    	public int countPrefix(String str){
    		if(str == null || str.length() == 0) return 0;
    		int len = str.length();
    		TreeNode curNode = root;
    		for(int i=0; i<len; i++){
    			char tmp = str.charAt(i);
    			int index = (int) tmp - '0';
    			if(curNode.next[index] == null){
    				return 0;
    			}
    			curNode = curNode.next[index];
    		}
    		return curNode.path;
    	}
    	
    	public int maxPrefixLen(String str){
    		if(str == null || str.length() == 0) return 0;
    		int len = str.length();
    		TreeNode curNode = root;
    		for(int i=0; i<len; i++){
    			char tmp = str.charAt(i);
    			int index = (int) tmp - '0';
    			if(curNode.next[index] == null){
    				return i;
    			}
    			curNode = curNode.next[index];
    		}
    		return len;
    	}
    	
    	public int countMaxPrefix(String str){
    		if(str == null || str.length() == 0) return 0;
    		int len = str.length();
    		TreeNode curNode = root;
    		for(int i=0; i<len; i++){
    			char tmp = str.charAt(i);
    			int index = (int) tmp - '0';
    			if(curNode.next[index] == null){
    				break;
    			}
    			curNode = curNode.next[index];
    		}
    		return curNode.path;
    	}
    }
    
    //主函数
    public class Main{
    	public static void main(String[] args) {
    		TrieTree tree = new TrieTree();
    		Scanner s = new Scanner(System.in);
    		int n = s.nextInt(); 
    		int q = s.nextInt();
    		while(n>0){
    			n--;
    			tree.insert(s.next());
    		}
    		
    		String[] qs = new String[q];
    		for(int i=0; i<q; i++){
    			qs[i] = s.next();
    		}
    		for(String str : qs){
    			System.out.println(tree.maxPrefixLen(str) + " " + tree.countMaxPrefix(str));
    		}
    		//test
                    //tree.insert("333333333333333333");
                    //tree.insert("111111122222222222");
                    //tree.insert("111111133333333333");
                    //		
                    //System.out.println(tree.maxPrefixLen("111111111000000000"));
                    //System.out.println(tree.maxPrefixLen("000000000000000000"));
                    //7
                    //0
    	}
    	
    }
    

    Reference:
    https://blog.csdn.net/xushiyu1996818/article/details/89354425

    第2题:添加与搜索单词

    添加与搜索单词 - 数据结构设计
    前缀树的边代表一个元素

    class WordDictionary {
        
        Tree root;
        /** Initialize your data structure here. */
        public WordDictionary() {
            root = new Tree();
        }
        
        public void addWord(String word) {
            Tree head = root;
            for(char c : word.toCharArray()){
                int t = c - 'a';
                if(head.nums[t] == null){
                    head.nums[t] = new Tree();
                }
                head = head.nums[t];
            }
            head.isOver = true;
        }
        
        public boolean search(String word) {
            Tree head = root;
            return dfs(word, 0, head);
        }
    
        private boolean dfs(String word, int index, Tree head){
            if(index == word.length()){
                if(head.isOver) return true;
                return false;
            }
            char c = word.charAt(index);
            if( c == '.' ){
                for(int i=0; i<26; i++){
                    if(head.nums[i] != null){
                        if(dfs(word, index+1, head.nums[i])){
                            return true;
                        }
                    }
                }
            }else{
                int t = c - 'a';
                if(head.nums[t] != null){
                    if(dfs(word, index+1, head.nums[t])){
                        return true;
                    }
                }
            }
            return false;
        }
    }
    
    class Tree{
        Tree [] nums;
        boolean isOver = false;
        
        public Tree(){
            nums = new Tree[26];
        }
    }
    

    第3题:实现Trie(前缀树)

    leetcode208. 实现Trie(前缀树)

    class Trie {
        private Node root;
    
        public Trie() {
            root = new Node();
        }
        
        public void insert(String word) {
            Node node = root;
            for(int i=0; i<word.length(); i++){
                char curC = word.charAt(i);
                if( !node.containsKey(curC)){
                    node.put(curC, new Node());
                }
                node = node.get(curC);
            }
            node.setEnd();
        }
        
        public boolean search(String word) {
            Node node = searchPrefix(word);
            return node != null && node.isEnd();
        }
        
        public boolean startsWith(String prefix) {
            Node node = searchPrefix(prefix);
            return node != null;
        }
    
        private Node searchPrefix(String word){
            Node node = root;
            for(int i=0; i<word.length(); i++){
                char c = word.charAt(i);
                if(node.containsKey(c)){
                    node = node.get(c);
                }else{
                    return null;
                }
            }
            return node;
        }
    }
    
    class Node{
        private Node[] links;
        private final int R = 26;
        private boolean end;
    
        public Node(){
            links = new Node[R];
        }
    
        public boolean containsKey(char c){
            return links[c - 'a'] != null;
        }
    
        public Node get(char c){
            return links[c-'a'];
        }
    
        public void put(char c, Node node){
            links[c-'a'] = node;
        }
    
        public void setEnd(){
            end = true;
        }
    
        public boolean isEnd(){
            return end;
        }
    }
    
    /**
     * Your Trie object will be instantiated and called as such:
     * Trie obj = new Trie();
     * obj.insert(word);
     * boolean param_2 = obj.search(word);
     * boolean param_3 = obj.startsWith(prefix);
     */
    
  • 相关阅读:
    Linux下查找文件(find、grep命令)及结合使用
    Linux x86 Program Start Up
    signal 信号具体含义解释(转)
    迁移Go mod使用笔记
    JS 中几种常用的循环方式
    JS 简单的表单验证功能
    微信小程序中的less开发公共样式引入
    vueelementadmin 超过两级嵌套路由无法缓存的解决办法
    PLM数据库脚本升级命令
    Git中的AutoCRLF与SafeCRLF
  • 原文地址:https://www.cnblogs.com/bacmive/p/15063384.html
Copyright © 2020-2023  润新知