1. 程式人生 > >LeetCode212. Word Search II

LeetCode212. Word Search II

問題描述

Given a 2D board and a list of words from the dictionary, find all words in the board.

Each word must be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.

For example,
Given words = ["oath","pea","eat","rain"] and board =

[
['o','a','a','n'],
['e','t','a','e'],
['i','h','k','r'],
['i','f','l','v']
]

Return ["eat","oath"].

問題分析

問題是給定一個String陣列,判斷陣列內的字串是否能夠在字元表中字元組成。比如eat可以由e(1,3),a(1,2),t(1,1)這三個元素組成。我們可以把這個表看作是圖中節點的關聯矩陣。那麼就是建立節點座標,然後對每個字串從頭到尾進行匹配就可以了。

程式碼如下

Java程式碼

 public List<String> findWords(char[][] board, String[] words) {
        List<int []>[] boardNode = new List[26];
        //獲取board行列
        int m = board.length,n = board[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int
b = board[i][j]-'a'; if(boardNode[b]==null){ boardNode[b]=new ArrayList<>(); } boardNode[b].add(new int[]{i,j}); } } Set<String> res= new HashSet<>(); for (String word:words ) { if(boardNode[word.charAt(0)-'a']!=null){ for (int[] child: boardNode[word.charAt(0)-'a'] ) { if(search(word,0,board,child[0],child[1],new boolean[m][n])){ res.add(word); break; } } } } return new ArrayList<>(res); } boolean search(String word,int index,char[][] board,int i,int j, boolean visited[][]){ //如果當前單元格已經完成 if(index==word.length()) return true; //如果越界或者當前單元格已經訪問過,返回False if(i==board.length||i<0) return false; if(j==board[0].length||j<0) return false; if(visited[i][j]) return false; if(word.charAt(index)==board[i][j]){ //如果當前單元格符合要求 visited[i][j]=true; //繼續遞迴 if(search(word,index+1,board,i+1,j,visited)||search(word,index+1,board,i-1,j,visited) || search(word,index+1,board,i,j+1,visited)|| search(word,index+1,board,i,j-1,visited)) return true; visited[i][j]=false; } return false; }

Java程式碼改進

因為上述程式碼的執行時間太差了,應該怎麼優化呢。上述程式碼因為對所有的字串都進行完整的判斷,如果出現兩個重複的字串asdasd,asdasd那麼仍然會完成兩個遞迴,如果倆個字串由大量的相同部分。asdasdfff,asdasdeee,也會完成兩個遞迴,這樣會浪費大量的時間,那麼如何對上述演算法進行優化呢。我們之前所寫的Trie型別就起到了作用,將字串陣列表達為Trie樹結構。對這個Trie樹進行判斷,這樣重複的部分就只需要進行一次遞迴就行了。

在遞迴過程中,如果發現符合條件的字串就新增到結果中

public List<String> findWords(char[][] board, String[] words) {
    List<String> res = new ArrayList<>();
    TrieNode root = buildTrieNode(words);
    for (int i = 0; i < board.length; i++) {
        for (int j = 0; j < board[0].length; j++) {
            dfs(board, i, j, root, res,new StringBuilder());
        }
    }
    return res;
}
void dfs(char[][] board,int i,int j,TrieNode trieNode,List<String> res,StringBuilder stringBuilder){
    char c = board[i][j];
    int ci = c-'a';
    //如果當前board已被使用或者不符合trie樹的結構
    if(c=='#'||trieNode.children[ci]==null) return;
    trieNode  = trieNode.children[ci];
    stringBuilder.append(c);
    if(trieNode.isEnd){
        res.add(stringBuilder.toString());
        trieNode.isEnd =false;
    }
    board[i][j]='#';
    if(i>0) dfs(board,i-1,j,trieNode,res,stringBuilder);
    if(j>0) dfs(board,i,j-1,trieNode,res,stringBuilder);
    if(j+1<board[0].length) dfs(board,i,j+1,trieNode,res,stringBuilder);
    if(i+1<board.length) dfs(board,i+1,j,trieNode,res,stringBuilder);
    stringBuilder.deleteCharAt(stringBuilder.length()-1);
    board[i][j]=c;
}
TrieNode buildTrieNode(String[] words){
    TrieNode root = new TrieNode();
    TrieNode node ;
    for (String word:words
            ) {
        node=root;
        for (int i = 0; i < word.length(); i++) {
            int c= word.charAt(i)-'a';
            if(node.children[c]==null) node.children[c]= new TrieNode();
            node = node.children[c];
        }
        node.isEnd=true;
    }
    return root;
}
class TrieNode{
    TrieNode[] children = new TrieNode[26];
    boolean isEnd = false;
}