给出一个2D板和字典中的单词列表,找出所有同时在2D板和字典中出现的单词。
每个单词必须由顺序相邻单元的字母构成,其中“相邻”单元是那些水平或垂直相邻的单元。同一个字母单元在一个单词中可能不会多次使用。
例如, 给出 words = [“oath”,”pea”,”eat”,”rain”] 和 board =
[
[‘o’,’a’,’a’,’n’],
[‘e’,’t’,’a’,’e’],
[‘i’,’h’,’k’,’r’],
[‘i’,’f’,’l’,’v’]
]
返回 [“eat”,”oath”]。
注意: 您可以假设所有输入都由小写字母 a-z 组成。
你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯?
如果当前单词不存在所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? Trie(前缀树) 如何?如果你想学习如何实现一个基本的前缀树,请先处理这个问题: 实施Trie(前缀树)。
这道题目最容易想到的就是穷举法:
找到所有匹配目标字符串的首字母,然后往相邻位置寻找,寻找的时候可以在2D板中把已经找到的字符标记,避免重复使用同一位置字符。
List list = new ArrayList();
public List findWords(char[][] board, String[] words) {
list = new ArrayList();
if(board.length > 0 && board[0].length > 0 && words.length > 0){
for(int i=0;ifor(int j=0;j0].length;j++){
for(int k=0;kif(board[i][j] == words[k].charAt(0)){
char c = board[i][j];
board[i][j] = '1';
findWords2(board,i,j, words[k] , words[k].substring(1));
board[i][j] = c;
}
}
}
}
return list;
}
return list;
}
public boolean findWords2(char[][] board,int i,int j, String word,String remain) {
if(remain.equals("") ){
if(!list.contains(word)){
list.add(word);
}
return true;
}
if(i-1 > -1 && board[i-1][j] != '1' && board[i-1][j] == remain.charAt(0)){
char c = board[i-1][j];
board[i-1][j] = '1';
if(findWords2(board,i-1, j, word, remain.substring(1))){
board[i-1][j] = c;
return true;
}
board[i-1][j] = c;
}
if(i+1 < board.length && board[i+1][j] != '1' && board[i+1][j] == remain.charAt(0)){
char c = board[i+1][j];
board[i+1][j] = '1';
if(findWords2(board,i+1, j, word, remain.substring(1))){
board[i+1][j] = c;
return true;
}
board[i+1][j] = c;
}
if(j-1 > -1 && board[i][j-1] != '1' && board[i][j-1] == remain.charAt(0)){
char c = board[i][j-1];
board[i][j-1] = '1';
if(findWords2(board,i, j-1, word, remain.substring(1))){
board[i][j-1] = c;
return true;
}
board[i][j-1] = c;
}
if(j+1 < board[0].length && board[i][j+1] != '1' && board[i][j+1] == remain.charAt(0)){
char c = board[i][j+1];
board[i][j+1] = '1';
if(findWords2(board,i, j+1, word, remain.substring(1))){
board[i][j+1] = c;
return true;
}
board[i][j+1] = c;
}
return false;
}
逻辑比较简单,但是效率不高,806 ms。
class Solution {
class TrieNode{
TrieNode next[] = new TrieNode[26];
public boolean isEnd;
public TrieNode(){}
}
class Trie {
private TrieNode head;
/** Initialize your data structure here. */
public Trie() {
head = new TrieNode();
}
public boolean search(String word) {
if(word == null || word.length() < 1){
return true;
}
if(head.next[word.charAt(0)-'a'] == null){
return false;
}
TrieNode cur = head.next[word.charAt(0)-'a'];
for(int i=1;iif(cur.next[word.charAt(i)-'a'] == null){
return false;
}
cur = cur.next[word.charAt(i)-'a'];
}
if(cur.isEnd){
return true;
}
return false;
}
/** Inserts a word into the trie. */
public void insert(String word) {
if(word != null && word.length() > 0 && head.next[word.charAt(0)-'a'] == null){
head.next[word.charAt(0)-'a'] = new TrieNode();
}
TrieNode cur = head.next[word.charAt(0)-'a'];
for(int i=1;iif(cur.next[word.charAt(i)-'a'] == null){
cur.next[word.charAt(i)-'a'] = new TrieNode();
}
cur = cur.next[word.charAt(i)-'a'];
}
cur.isEnd = true;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
if(prefix == null || prefix.length() < 1){
return true;
}
if(head.next[prefix.charAt(0)-'a'] == null){
return false;
}
TrieNode cur = head.next[prefix.charAt(0)-'a'];
for(int i=1;iif(cur.next[prefix.charAt(i)-'a'] == null){
return false;
}
cur = cur.next[prefix.charAt(i)-'a'];
}
return true;
}
}
Set set = new HashSet();
public List findWords(char[][] board, String[] words) {
set.clear();
List resList = new ArrayList();
if(board.length > 0 && board[0].length > 0 && words.length > 0){
Trie t = new Trie();
for(int i=0;ifor(int i=0;ifor(int j=0;j0].length;j++){
char c = board[i][j];
board[i][j] = '1';
findWords2(board,i,j, t, c+"");
board[i][j] = c;
}
}
for(int i=0;iif(set.contains(words[i]) && !resList.contains(words[i])){
resList.add(words[i]);
}
}
return resList;
}
return resList;
}
public void findWords2(char[][] board,int i,int j,Trie t,String curStr) {
if(t.search(curStr)){
set.add(curStr);
}
if(t.startsWith(curStr)){
if(i-1 > -1 && board[i-1][j] != '1'){
char c = board[i-1][j];
board[i-1][j] = '1';
findWords2(board,i-1, j, t, curStr+c);
board[i-1][j] = c;
}
if(i+1 < board.length && board[i+1][j] != '1'){
char c = board[i+1][j];
board[i+1][j] = '1';
findWords2(board,i+1, j, t, curStr+c);
board[i+1][j] = c;
}
if(j-1 > -1 && board[i][j-1] != '1'){
char c = board[i][j-1];
board[i][j-1] = '1';
findWords2(board,i, j-1, t, curStr+c);
board[i][j-1] = c;
}
if(j+1 < board[0].length && board[i][j+1] != '1'){
char c = board[i][j+1];
board[i][j+1] = '1';
findWords2(board,i, j+1, t, curStr+c);
board[i][j+1] = c;
}
}
return ;
}
}
用前缀树直接将用时减少到71ms