trie字典树可以用来查找单词或者搜索剪枝用。
Implement Trie (Prefix Tree)
实现一个 Trie,包含 insert
, search
, 和 startsWith
这三个方法。
思路:模板必须记住;没有儿子建立儿子,有儿子走儿子;
public class Trie {
private class TrieNode {
TrieNode[] children;
boolean isword;
String word;
public TrieNode () {
this.children = new TrieNode[26];
for(int i = 0; i < 26; i++){
children[i] = null;
}
this.isword = false;
this.word = null;
}
}
private TrieNode root;
public Trie() {
root = new TrieNode();
}
/*
* @param word: a word
* @return: nothing
*/
public void insert(String word) {
if(word == null) {
return;
}
TrieNode cur = root;
for(int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if(cur.children[c - 'a'] == null) {
cur.children[c - 'a'] = new TrieNode();
}
cur = cur.children[c - 'a'];
}
cur.isword = true;
cur.word = word;
}
/*
* @param word: A string
* @return: if the word is in the trie.
*/
public boolean search(String word) {
if(word == null) {
return false;
}
TrieNode cur = root;
for(int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if(cur.children[c - 'a'] == null){
return false;
}
cur = cur.children[c - 'a'];
}
return cur.isword && cur.word.equals(word);
}
/*
* @param prefix: A string
* @return: if there is any word in the trie that starts with the given prefix.
*/
public boolean startsWith(String prefix) {
if(prefix == null) {
return false;
}
TrieNode cur = root;
for(int i = 0; i < prefix.length(); i++) {
char c = prefix.charAt(i);
if(cur.children[c - 'a'] == null){
return false;
}
cur = cur.children[c - 'a'];
}
return cur != root;
}
}
Add and Search Word - Data structure design
设计一个包含下面两个操作的数据结构:addWord(word)
, search(word)
addWord(word)
会在数据结构中添加一个单词。而search(word)
则支持普通的单词查询或是只包含.
和a-z
的简易正则表达式的查询。
一个 .
可以代表一个任何的字母。
Example
样例 1:
输入:
addWord("a")
search(".")
输出:
true
样例 2:
输入:
addWord("bad")
addWord("dad")
addWord("mad")
search("pad")
search("bad")
search(".ad")
search("b..")
输出:
false
true
true
true
Notice
你可以认为所有的单词都只包含小写字母 a-z。
思路:遇见 ‘.’ 之后,for循环check每一个可能性;注意这题我写了两个坑:
1. index == word.length()的时候,返回的是cur.isword, 而不是直接返回true;
2. for循环的时候,一定要判断cur.children[i] != null, 也就是判断单词是否插入到这条路径上了,否则就是null;不需要继续搜索;
public class WordDictionary {
/*
* @param word: Adds a word into the data structure.
* @return: nothing
*/
private class TrieNode {
private TrieNode[] children;
private boolean isword;
private String word;
public TrieNode () {
children = new TrieNode[26];
for(int i = 0; i < 26; i++){
children[i] = null;
}
this.isword = false;
this.word = null;
}
}
private TrieNode root;
public WordDictionary() {
root = new TrieNode();
}
public void addWord(String word) {
if(word == null) {
return;
}
TrieNode cur = root;
for(int i = 0; i < word.length(); i++){
char c = word.charAt(i);
if(cur.children[c - 'a'] == null) {
cur.children[c - 'a'] = new TrieNode();
}
cur = cur.children[c - 'a'];
}
cur.isword = true;
cur.word = word;
}
/*
* @param word: A word could contain the dot character '.' to represent any one letter.
* @return: if the word is in the data structure.
*/
public boolean search(String word) {
if(word == null) {
return false;
}
return dfs(word, 0, root);
}
private boolean dfs(String word, int index, TrieNode cur) {
if(index == word.length()){
return cur.isword; //两个坑,第一,这里返回最后一个node 是不是isword;
}
char c = word.charAt(index);
if(c == '.') {
for(int i = 0; i < 26; i++) {
// 第二,这里需要判断cur.children[i]是否是null;
// 为什么,是为了防止根本没有节点input到这个node下面,也就是dictionary里面根本没有;
if(cur.children[i] != null) {
if(dfs(word, index+1, cur.children[i])) {
return true;
}
}
}
return false;
} else {
if(cur.children[c - 'a'] == null) {
return false;
} else {
return dfs(word, index+1, cur.children[c - 'a']);
}
}
}
}
trie的剪枝
Word Search II
给出一个由小写字母组成的矩阵和一个字典。找出所有同时在字典和矩阵中出现的单词。一个单词可以从矩阵中的任意位置开始,可以向左/右/上/下四个相邻方向移动。一个字母在一个单词中只能被使用一次。且字典中不存在重复单词
Example
样例 1:
输入:["doaf","agai","dcan"],["dog","dad","dgdg","can","again"]
输出:["again","can","dad","dog"]
解释:
d o a f
a g a i
d c a n
矩阵中查找,返回 ["again","can","dad","dog"]。
思路:其实传递trietree进去,就已经相当于把所有的word全部传进去了,那么对于每个 x,y,那么只需要在这个点展开,搜索所有可能的string就可以了。参数传递不需要用word,只需要用trietree,因为是搜所有可能的word;
public class Solution {
/**
* @param board: A list of lists of character
* @param words: A list of string
* @return: A list of string
*/
private class TrieNode {
public TrieNode[] children;
public boolean isword;
public String word;
public TrieNode() {
children = new TrieNode[26];
for(int i = 0; i < 26; i++) {
children[i] = null;
}
this.isword = false;
this.word = null;
}
}
private class TrieTree {
TrieNode root;
public TrieTree() {
root = new TrieNode();
}
public void insert(String word) {
if(word == null) {
return;
}
TrieNode cur = root;
for(int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if(cur.children[c - 'a'] == null) {
cur.children[c - 'a'] = new TrieNode();
}
cur = cur.children[c - 'a'];
}
cur.isword = true;
cur.word = word;
}
}
public List<String> wordSearchII(char[][] board, List<String> words) {
List<String> result = new ArrayList<String>();
if(board == null || board.length == 0 || board[0].length == 0 || words == null) {
return result;
}
// build trie tree;
TrieTree trieTree = new TrieTree();
for(String word: words) {
trieTree.insert(word);
}
int n = board.length;
int m = board[0].length;
boolean[][] visited = new boolean[n][m];
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
// trieTree 相当于把 words全部传进去了;
search(board, i, j, trieTree.root, result, visited);
}
}
return result;
}
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
private void search(char[][] board, int x, int y, TrieNode cur,
List<String> result, boolean[][] visited) {
if(!isvalid(board, x, y, visited)) {
return;
}
char c = board[x][y];
TrieNode child = cur.children[c-'a'];
if(child == null) {
return;
}
// 注意这里是判断child的word是否是word,因为走了一步了;
if(child.isword && child.word != null) {
if(!result.contains(child.word)) {
result.add(child.word);
// 而且这里搜集了一个单词之后,不需要return,继续搜下面的,下面也许还有单词;
}
}
visited[x][y] = true;
for(int k = 0; k < 4; k++) {
int nx = x + dx[k];
int ny = y + dy[k];
if(isvalid(board, nx, ny, visited)) {
search(board, nx, ny, child, result, visited);
}
}
visited[x][y] = false;
}
private boolean isvalid(char[][] board, int nx, int ny, boolean[][] visited) {
return (0 <= nx && nx < board.length && 0 <= ny && ny < board[0].length && !visited[nx][ny]);
}
}
Word Squares
给出一系列 不重复的单词,找出所有用这些单词能构成的 单词矩阵。
一个有效的单词矩阵是指, 如果从第 k 行读出来的单词和第 k 列读出来的单词相同(0 <= k < max(numRows, numColumns)),那么就是一个单词矩阵.
例如,单词序列为 ["ball","area","lead","lady"]
,构成一个单词矩阵。因为对于每一行和每一列,读出来的单词都是相同的。
b a l l
a r e a
l e a d
l a d y
Example
样例 1:
输入:
["area","lead","wall","lady","ball"]
输出:
[["wall","area","lead","lady"],["ball","area","lead","lady"]]
解释:
输出包含 两个单词矩阵,这两个矩阵的输出的顺序没有影响(只要求矩阵内部有序)。
思路:如何利用trie剪枝就是通过已经加入的单词list,构造出后面即将加入单词的prefix list,然后从prefix list中选取candidate;
public class Solution {
/*
* @param words: a set of words without duplicates
* @return: all word squares
*/
private class TrieNode {
public TrieNode[] children;
public List<String> prefixList;
public boolean isword;
public String word;
public TrieNode () {
children = new TrieNode[26];
prefixList = new ArrayList<String>();
isword = false;
word = null;
}
}
private class TrieTree {
private TrieNode root;
public TrieTree() {
root = new TrieNode();
}
public void insert(String word) {
if(word == null) {
return;
}
TrieNode cur = root;
for(int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if(cur.children[c - 'a'] == null) {
cur.children[c - 'a'] = new TrieNode();
}
cur = cur.children[c - 'a'];
cur.prefixList.add(word);
}
cur.isword = true;
cur.word = word;
}
public List<String> getPrefixList(String prefix) {
List<String> res = new ArrayList<String>();
if(prefix == null) {
return res;
}
TrieNode cur = root;
for(int i = 0; i < prefix.length(); i++) {
char c = prefix.charAt(i);
if(cur.children[c - 'a'] == null) {
return res;
}
cur = cur.children[c - 'a'];
}
res.addAll(cur.prefixList);
return res;
}
}
public List<List<String>> wordSquares(String[] words) {
List<List<String>> lists = new ArrayList<List<String>>();
if(words == null || words.length == 0) {
return lists;
}
TrieTree trieTree = new TrieTree();
for(String word: words) {
trieTree.insert(word);
}
int n = words[0].length();
List<String> list = new ArrayList<String>();
for(String word: words) {
list.add(word);
dfs(trieTree, n, word, lists, list);
list.remove(list.size() -1);
}
return lists;
}
private void dfs(TrieTree trieTree, int n, String word,
List<List<String>> lists, List<String> list) {
if(list.size() == n) {
lists.add(new ArrayList<String>(list));
return;
}
// 通过已经加入的单词,组成后面单词的prefix,来剪枝的;
// 这样就规范了矩阵的顺序,能够排列成square;
StringBuilder sb = new StringBuilder();
int idx = list.size();
for(int i = 0; i < list.size(); i++) {
sb.append(list.get(i).charAt(idx));
}
String prefix = sb.toString();
for(String str: trieTree.getPrefixList(prefix)){
list.add(str);
dfs(trieTree, n, str, lists, list);
list.remove(list.size() - 1);
}
}
}