前缀树算法模板秒杀五道算法题 :: labuladong的算法小抄 #1032
Replies: 27 comments 5 replies
-
OMG |
Beta Was this translation helpful? Give feedback.
-
小板凳坐着 |
Beta Was this translation helpful? Give feedback.
-
真的OMG。。。。Trie太复杂了也。。还不如拆开来说。。 |
Beta Was this translation helpful? Give feedback.
-
208题,R值设置为英文字母z的ascii + 1 == 123即可 |
Beta Was this translation helpful? Give feedback.
-
@bhjghjbhbfghfhfg 理论上 27 就够了,自己去优化就好。 |
Beta Was this translation helpful? Give feedback.
-
getNode那里的泛型是不是有点问题,直接对模板T的数组children赋char了 |
Beta Was this translation helpful? Give feedback.
-
@Jebearssica |
Beta Was this translation helpful? Give feedback.
-
648题 单词替换 代码部分
中间需要进行一次判空操作 , String.isEmpty() 不能对空判断 |
Beta Was this translation helpful? Give feedback.
-
hasKeyWithPattern 超出时间限制 需要维护一个额外记录 |
Beta Was this translation helpful? Give feedback.
-
@zhongweiLeex 贴出你的代码 |
Beta Was this translation helpful? Give feedback.
-
class WordDictionary {
private Node root = null;
private int size = 0;
public WordDictionary() {
}
private static class Node {
boolean end = false;
Node[] children = new Node[26];
}
private Node getNode(Node node , String key){
Node p = root;
for (int i = 0; i < key.length(); i++) {
if (p == null){
return null;
}
char c = key.charAt(i);
p = p.children[c-'a'];
}
return p;
}
public boolean get(String key){
Node p = getNode(root,key);
if (p == null || p.end == false){
return false;
}
return p.end;
}
public void addWord(String word) {
root = addWord(root,word,0);
}
private Node addWord(Node node , String key, int i){
if (node == null){
node = new Node();
}
if (i == key.length()){
node.end = true;
return node;
}
char c= key.charAt(i);
node.children[c-'a'] = addWord(node.children[c-'a'],key,i+1);
return node;
}
public boolean search(String word) {
char[] pattern = word.toCharArray();
return search(root,pattern,0);
}
private boolean search(Node node,char[] pattern,int i){
if(node == null){
return false;
}
if(i == pattern.length){
return node.end;
}
char c = pattern[i];
if (c == '.'){
for (int j = 0; j < 26; j++) {
if(node == null){
return false;
}
if (node.children[j] == null){
continue;
}
Node newNode = node.children[j];
if (search(newNode,pattern,i+1)){
return true;
}
}
}else{
int k = pattern[i] - 'a';
if(node == null){
return false;
}
if (node.children[k] == null){
return false;
}
node = node.children[k];
if (search(node,pattern,i+1)){
return true;
}
}
return false;
}
}
/**
* Your WordDictionary object will be instantiated and called as such:
* WordDictionary obj = new WordDictionary();
* obj.addWord(word);
* boolean param_2 = obj.search(word);
*/ |
Beta Was this translation helpful? Give feedback.
-
当Trie中只有一个元素的时候,执行remove会不会递归把root结点也删除掉? |
Beta Was this translation helpful? Give feedback.
-
打卡,感谢! |
Beta Was this translation helpful? Give feedback.
-
我 |
Beta Was this translation helpful? Give feedback.
-
请问set 和 map有特别具体的区别吗? 我搜了一下感觉依然云里雾里 |
Beta Was this translation helpful? Give feedback.
-
TrieSet 和TrieMap 的区别又是什么呢? 我觉得所有题目都可以用map直接写呀 |
Beta Was this translation helpful? Give feedback.
-
其实node里面不用数组,直接用hashmap就行,还省空间 |
Beta Was this translation helpful? Give feedback.
-
感觉648直接用string 的join比StringBuilder省事一点呢 class Solution {
public String replaceWords(List<String> dictionary, String sentence) {
// put the dict in a trie set and use the shortestPrefixOf method
TrieSet dict = new TrieSet();
for (String root : dictionary) {
dict.add(root);
}
String[] words = sentence.split(" ");
for (int i = 0; i < words.length; ++i) {
String prefix = dict.shortestPrefixOf(words[i]);
// if prefix can be found, replace
if (!prefix.isEmpty()) {
words[i] = prefix;
}
}
return String.join(" ", words);
}
} |
Beta Was this translation helpful? Give feedback.
-
211 只要把children 数组大小优化成26就不会超时啦 class WordDictionary {
private final TrieMap<Object> map;
public WordDictionary() {
map = new TrieMap<>();
}
public void addWord(String word) {
// use key to store word, Object as placehoder
map.put(word, new Object());
}
public boolean search(String word) {
// has '.'
return map.hasKeyWithPattern(word);
}
}
class TrieMap<V> {
private static final int R = 26;
private int size = 0;
private class TrieNode<V> {
V val = null;
TrieNode<V>[] children = new TrieNode[R];
}
private TrieNode<V> root = null;
// methods
public void put(String word, V val) {
if (!containsKey(word)) {
size++;
}
root = put(root, word, val, 0);
}
private TrieNode<V> put(TrieNode<V> node, String key, V val, int i) {
if (node == null) {
node = new TrieNode<>();
}
if (i == key.length()) {
node.val = val;
return node;
}
char c = key.charAt(i);
node.children[c - 'a'] = put(node.children[c - 'a'], key, val, i + 1);
return node;
}
public boolean hasKeyWithPattern(String pattern) {
return hasKeyWithPattern(root, pattern, 0);
}
// recursion helper
private boolean hasKeyWithPattern(TrieNode<V> node, String pattern, int i) {
if (node == null) {
// cannot continue
return false;
}
if (i == pattern.length()) {
// reaches the end
return node.val != null;
}
char c = pattern.charAt(i);
if (c != '.') {
// if not .
return hasKeyWithPattern(node.children[c - 'a'], pattern, i + 1);
}
for (int j = 0; j < R; ++j) {
// see every possible char, return if any has val
if (hasKeyWithPattern(node.children[j], pattern, i + 1)) {
// as long as one child has this patten
return true;
}
// NOTE: NOT return hasKeyWithPattern(node.children[j], pattern, i + 1);
}
// nothing matched
return false;
}
private TrieNode<V> getNode(TrieNode<V> node, String key) {
// search downwards from node
TrieNode<V> p = node;
for (int i = 0; i < key.length(); ++i) {
if (p == null) {
return null;
}
char c = key.charAt(i);
p = p.children[c - 'a'];
}
return p;
}
private V get(String key) {
TrieNode<V> x = getNode(root, key);
if(x == null || x.val == null) {
return null;
}
return x.val;
}
private boolean containsKey(String key) {
return get(key) != null;
}
} |
Beta Was this translation helpful? Give feedback.
-
迷惑,,我直接套到design-add-and-search-words-data-structure里面,遇到pattern直接返回竟然比返回所有结果还慢很多,有的尝试直接TLE |
Beta Was this translation helpful? Give feedback.
-
好吧,力扣运行速度浮动性太大了,直接从TLE到faster than 85了 |
Beta Was this translation helpful? Give feedback.
-
有一点没太想清楚,看文章中求字符串最短前缀时,返回的是query.substring(0,i),但是看完后自己做648题时,发现我的方法中应该是i+1,然后把文章中的TrieMap和TrieSet全部拷进去运行结果也是对的,半天没有想清楚哪里不一样,下面是我的答案。 public class Solution648 {
static class TrieNode {
boolean isWord;
TrieNode[] children = new TrieNode[26];
}
TrieNode root = new TrieNode();
public String replaceWords(List<String> dictionary, String sentence) {
for (String str : dictionary) {
add(str);
}
StringBuilder sb = new StringBuilder();
for (String str : sentence.split(" ")) {
sb.append(shortestPrefixOf(str)).append(" ");
}
return sb.substring(0, sb.length() - 1);
}
private String shortestPrefixOf(String query) {
TrieNode p = root;
for (int i = 0; i < query.length(); i++) {
int idx = query.charAt(i) - 'a';
if (p.children[idx] == null) {
break;
}
if (p.children[idx].isWord) {
return query.substring(0, i + 1);//就是这里
}
p = p.children[idx];
}
return query;
}
private void add(String s) {
TrieNode p = root;
for (int i = 0; i < s.length(); i++) {
int idx = s.charAt(i) - 'a';
if (p.children[idx] == null) {
p.children[idx] = new TrieNode();
}
p = p.children[idx];
}
p.isWord = true;
}
} |
Beta Was this translation helpful? Give feedback.
-
明白了,我判断的是它的孩子节点的值,文章中判断的是它本身的值,这两种情况中的i是不一样的 |
Beta Was this translation helpful? Give feedback.
-
class TrieNode:
R = 256
def __init__(self):
self.val = None
self.children = [None for _ in range(self.R)]
class TrieMap:
R = 256
def __init__(self):
self.root = None
self.size = 0
def get_node(self, node: TrieNode, key: str) -> TrieNode|None:
"""从node开始搜索key, 若果存在返回对应节点,否则返回None"""
p = node
for i in range(len(key)):
if p is None:
return None
c = ord(key[i])
p = p.children[c]
return p
def put(self, key: str, val: any) -> None:
if not self.contains_key(key):
self.size += 1
self.root = self._put(self.root, key, val, 0)
def _put(self, node: TrieNode, key: str, val: any, i: int):
if not node:
node = TrieNode()
if i == len(key):
node.val = val
return node
c = ord(key[i])
node.children[c] = self._put(node.children[c], key, val, i+1)
return node
def remove(self, key: str):
"""
删除key
:param key:
:return:
"""
if not self.contains_key(key):
return
self.root = self._remove(self.root, key, 0)
self.size -= 1
def _remove(self, node: TrieNode, key: str, i: int):
if not node:
return None
if i == len(key):
node.val = None
else:
c = ord(key[i])
node.children[c] = self._remove(node.children[c], key, i+1)
if node.val:
return node
for c in range(self.R):
if node.children[c]:
return node
return None
def get(self, key: str):
x = self.get_node(self.root, key)
if x is None or x.val is None:
return None
return x.val
def contains_key(self, key: str) -> bool:
"""
判断key是否存在map中
:param key:
:return:
"""
return bool(self.get(key))
def has_key_with_prefix(self, prefix: str):
"""
判断是否存在前缀为prefix的key
:param prefix:
:return:
"""
return bool(self.get_node(self.root, prefix))
def shortest_prefix_of(self, query: str) -> str:
"""
从所有key中寻找query的最短前缀
:param query:
:return:
"""
p = self.root
for i in range(len(query)):
if p is None:
return ""
if p.val is not None:
return query[:i]
c = ord(query[i])
p = p.children[c]
if p is not None and p.val is not None:
# query本身就是一个key
return query
return ""
def longest_prefix_of(self, query: str) -> str:
"""
query最长前缀
:param query:
:return:
"""
p = self.root
max_len = 0
for i in range(len(query)):
if p is None:
break
if p.val is not None:
max_len = i
c = ord(query[i])
p = p.children[c]
if p is not None and p.val is not None:
return query
return query[:max_len]
def keys_with_prefix(self, prefix: str) -> List:
res = []
x = self.get_node(self.root, prefix)
if x is None:
return res
# dfs遍历x为根的树
self._traverse(x, [], res)
return res
def _traverse(self, node: TrieNode, path: List, res: List):
if not node:
return
if node.val:
res.append(''.join(path))
for c in range(self.R):
path.append(c)
self._traverse(node.children[ord(c)], path, res)
path.pop()
def has_key_with_pattern(self, pattern):
return self._has_key_with_pattern(self.root, pattern, 0)
def _has_key_with_pattern(self, node: TrieNode, pattern: str, i):
"""
匹配通配符是否存在key
:param node:
:param pattern:
:param i:
:return:
"""
if not node:
return False
if i == len(pattern):
# 到头了
return bool(node.val)
c = pattern[i]
if c != '.':
return self._has_key_with_pattern(node.children[ord(c)], pattern, i+1)
# 遇到通配符 .
for j in range(self.R):
if self._has_key_with_pattern(node.children[j], pattern, i+1):
return True
return False
def keys_with_pattern(self, pattern: str):
"""匹配所有符合pattern的key"""
res = []
self._keys_with_pattern(self.root, [], pattern, 0, res)
return res
def _keys_with_pattern(self, node: TrieNode, path: List, pattern: str, i: int, res: List):
if not node:
return
if i == len(pattern):
# pattern 匹配完成
if node.val:
res.append(''.join(path))
return
c = pattern[i]
if c == '.':
for j in range(self.R):
path.append(chr(j))
self._keys_with_pattern(node.children[j], path, pattern, i+1, res)
path.pop()
else:
path.append(c)
self._keys_with_pattern(node.children[ord(c)], path, pattern, i+1, res)
path.pop()
def __len__(self):
return self.size |
Beta Was this translation helpful? Give feedback.
-
leetcode 211题不超时的代码,按照东哥的思路写的 class WordDictionary {
private static class TrieNode{
private boolean isEnd=false;
private TrieNode[] children=new TrieNode[26];
}
TrieNode root=null;
public WordDictionary() {
root=new TrieNode();
}
public void addWord(String word) {
root=put(root,word,0);
}
private TrieNode put(TrieNode node,String word,int i){
if(node==null){
node=new TrieNode();
}
if(i==word.length()){
node.isEnd=true;
return node;
}
int c=word.charAt(i)-'a';
node.children[c]=put(node.children[c],word,i+1);
return node;
}
public boolean search(String word) {
return hasKeyWithPattern(root,word,0);
}
private boolean hasKeyWithPattern(TrieNode node,String word,int i){
if(node==null){
return false;
}
if(i==word.length()){
return node.isEnd;
}
char c=word.charAt(i);
if(c!='.'){
int b=c-'a';
return hasKeyWithPattern(node.children[b],word,i+1);
}
for(int j=0;j<26;j++){
if(hasKeyWithPattern(node.children[j],word,i+1)){
return true;
}
}
return false;
}
}
/**
* Your WordDictionary object will be instantiated and called as such:
* WordDictionary obj = new WordDictionary();
* obj.addWord(word);
* boolean param_2 = obj.search(word);
*/ |
Beta Was this translation helpful? Give feedback.
-
好复杂啊啊啊 |
Beta Was this translation helpful? Give feedback.
-
Memory Limit Exceeded,该怎么办啊啊啊 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
文章链接点这里:前缀树算法模板秒杀五道算法题
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions