前缀树
从一道较为简单的题来理解
> **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(前缀树)
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);
*/