LintCode-132: Word Search II (DFS深搜经典难题!)

  1. Word Search II
    中文English
    Given a matrix of lower alphabets and a dictionary. Find all words in the dictionary that can be found in the matrix. A word can start from any position in the matrix and go left/right/up/down to the adjacent position. One character only be used once in one word. No same word in dictionary

Example
Example 1:

Input:[“doaf”,“agai”,“dcan”],[“dog”,“dad”,“dgdg”,“can”,“again”]
Output:[“again”,“can”,“dad”,“dog”]
Explanation:
d o a f
a g a i
d c a n
search in Matrix,so return [“again”,“can”,“dad”,“dog”].
Example 2:

Input:[“a”],[“b”]
Output:[]
Explanation:
a
search in Matrix,return [].
Challenge
Using trie to implement your algorithm.

解法1:
这题挺难的。我参考的网上的答案。
思路就是暴力深搜 - 每个格子来一次DFS深搜。
注意:

  1. visited[]数组还是需要
  2. 注意剪枝:如果不是prefix,剪掉! 如果出界或已经访问过,剪掉!
  3. char -> string 不能直接用std里面的to_string(),因为char本来就是integer,to_string(char)会生成"100","103"这样的字符串。可以用string(1,c)来生成一个临时的内容为c,长度为1的字符串。
  4. 对于二维数组或vector,用if (board.size() < 1) 比if (board.empty()) 更好
  5. getPrefixSets()里面,下面一行
    if (prefixSets.find(prefix) == prefixSets.end())
    很关键!因为有可能某个word刚好是之前某个prefix,这样可以避免该word被关联到false!
class Solution {
public:
    /**
     * @param board: A list of lists of character
     * @param words: A list of string
     * @return: A list of string
     */
    vector<int> dx = {0, 1, -1, 0};
    vector<int> dy = {1, 0, 0, -1};
    map<string, bool> prefixSets; //放所有的prefix,如果是word就对应true,否则对应false
    
    vector<string> wordSearchII(vector<vector<char>> &board, vector<string> &words) {
        vector<string> result;
        
        if (board.size() < 1)  //比if (board.empty()) 更好?
            return result;
        
        vector<vector<bool>> visited;
        for (int i = 0; i < board.size(); ++i) 
            visited.push_back(vector<bool>(board[i].size(), false));

        getPrefixSets(words);

        set<string> wordSet;
        
        for (int i = 0; i < board.size(); ++i) {
            for (int j = 0; j < board[i].size(); ++j) {
                visited[i][j] = true;
                //char->string 不可以用to_string,否则就是 100->"100"
                helper(board, visited, i, j, string(1, board[i][j]), wordSet); //注意string(1,c)
                visited[i][j] = false;
            }
        }
        
        for (auto w : wordSet)
            result.push_back(w);

        return result;
    }
    
private:
    //generate the map
    void getPrefixSets(vector<string> &words) {
        for (auto w : words) {
            for (int i = 0; i < w.size() - 1; ++i) {  //不需要到w.size(),因为整个word下面会处理
                string prefix = w.substr(0, i + 1); //前i + 1个字符
                if (prefixSets.find(prefix) == prefixSets.end()) //关键!有可能某个word刚好是之前某个prefix,这样可以避免该word被关联到false!
                    prefixSets[prefix] = false;
            }
            prefixSets[w] = true;
        }
    }
    
    void helper(vector<vector<char>> &board, vector<vector<bool>> &visited, int x, int y, string word, set<string> &wordSet) {
        if (prefixSets.find(word) == prefixSets.end())  //如果不是prefix, 剪枝
            return;
        
        if (prefixSets[word]) 
            wordSet.insert(word);
    
        for (int i = 0; i < 4; ++i) {
            int adjX = x + dx[i];
            int adjY = y + dy[i];
            
            if (!inside(board, adjX, adjY) || visited[adjX][adjY])  // 出界或已访问,剪枝
                continue;
            
            visited[adjX][adjY] = true;
            helper(board, visited, adjX, adjY, word + board[adjX][adjY], wordSet);
            visited[adjX][adjY] = false;
        }
    }
    
    bool inside(vector<vector<char>> &board, int x, int y) {
        return x >= 0 &&
               x < board.size() &&
               y >= 0 &&
               y < board[0].size();
    }        
};

二刷:

class Solution {
public:
    /**
     * @param board: A list of lists of character
     * @param words: A list of string
     * @return: A list of string
     *          we will sort your return value in output
     */
    vector<string> wordSearchII(vector<vector<char>> &board, vector<string> &words) {
        unordered_set<string> wordSets;
        unordered_set<string> preSets;
        for (auto word : words) {
            string pre = "";
            for (int i = 0; i < word.size(); i++) {
                pre = pre + word[i];
                preSets.insert(pre);
            }
            wordSets.insert(pre);
        }
        unordered_set<string> sols;
        for (int i = 0; i < board.size(); i++) {
            for (int j = 0; j < board[0].size(); j++) {
                vector<vector<bool>> visited(board.size(), vector<bool>(board[0].size(), false));
                string sol(1, board[i][j]);
                visited[i][j] = true;
                helper(board, wordSets, preSets, i, j, visited, sol, sols);
                visited[i][j] = false;
                sol.pop_back();
            }
        }
        vector<string> res(sols.begin(), sols.end());
        return res;
    }
private:
    void helper(vector<vector<char>> &board, unordered_set<string> &wordSets, unordered_set<string> &preSets,
                int currX, int currY, vector<vector<bool>> &visited, 
                string sol, unordered_set<string> &sols) {
        if (preSets.find(sol) == preSets.end()) return;                
        if (wordSets.find(sol) != wordSets.end() && sols.find(sol) == sols.end()) {
            sols.insert(sol);
        //    return;  //do not stop! continue to search
        }
        for (int i = 0; i < 4; i++) {
            int newX = currX + dx[i];
            int newY = currY + dy[i];
            if (newX < 0 || newX >= board.size() ||
                newY < 0 || newY >= board[0].size() ||
                visited[newX][newY]) continue;
            //sol = sol + board[newX][newY];
            if (preSets.find(sol) != preSets.end()) {
                visited[newX][newY] = true;
                helper(board, wordSets, preSets, newX, newY, visited, sol + board[newX][newY], sols);
                visited[newX][newY] = false;
                //sol.pop_back();  //important
            } //else {
            //    sol.pop_back();
            //}
        }
        return;
    }
    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {-1, 1, 0, 0};
};

解法2:trie+dfs

struct Node {
    Node *ch[26];
    string str;
    Node() {
        for (int i = 0; i < 26; i++) {
            ch[i] = nullptr;
        }
        str = "";
    }
};

class Solution {
public:
    /**
     * @param board: A list of lists of character
     * @param words: A list of string
     * @return: A list of string
     *          we will sort your return value in output
     */
    vector<string> wordSearchII(vector<vector<char>> &board, vector<string> &words) {
        Node *trieRoot = new Node();
        
        for (auto word : words) {
            insert(trieRoot, word);
        }

        unordered_set<string> sols;
        for (int i = 0; i < board.size(); i++) {
            for (int j = 0; j < board[0].size(); j++) {
                vector<vector<bool>> visited(board.size(), vector<bool>(board[0].size(), false));
                
                if (trieRoot->ch[board[i][j] - 'a'] != nullptr) {
                    visited[i][j] = true;
                    search(board, trieRoot->ch[board[i][j] - 'a'], i, j, visited, sols);
                    visited[i][j] = false;
                }
            }
        }
        vector<string> res(sols.begin(), sols.end());
        return res;
    }
private:
    void insert(Node *p, string s) {
        int len = s.size();
        for (int i = 0; i < len; i++) {
            if (p->ch[s[i] - 'a'] == nullptr) {
                p->ch[s[i] - 'a'] = new Node();
            }
            p = p->ch[s[i] - 'a'];
        }
        p->str = s; //store the string at the last node
       // cout << " insert " << p->str << endl;
    }

    void search(vector<vector<char>> &board, Node *node, int x, int y, vector<vector<bool>> &visited, unordered_set<string> &sols) {
        if (!node->str.empty()) {
            sols.insert(node->str);
            //p->str = "";    //can also clear p->str here so there would be no duplicate 
                              //(i.e., we do not need to use set or unordered_set, vector<string> sols is OK)
        }
        for (int i = 0; i < 4; i++) {
            int newX = x + dx[i];
            int newY = y + dy[i];
            if (newX < 0 || newX >= board.size() || newY < 0 || newY >= board[0].size() || visited[newX][newY]) continue;
            if (node->ch[board[newX][newY] - 'a'] != nullptr) {
                visited[newX][newY] = true;
                search(board, node->ch[board[newX][newY] - 'a'], newX, newY, visited, sols);
                visited[newX][newY] = false;
            }
        }
    }

    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
};

注意:

  1. 排重。否则下面的输入例子会有两个"dad"。
    Explanation:
    d o a f
    a g a i
    d c a n
    排重可用set或unordered_set,然后再将结果传给一个vector返回。也可以在得到一个node->str后,将node->str设为"",这样下一次就不会再得到这个node->str。

二刷:

struct TrieNode {
    bool isWord;
    TrieNode * children[26];
    TrieNode() : isWord(false) {
        for (int i = 0; i < 26; i++) {
            children[i] = NULL;
        }
    }
};

class Solution {
public:
    /**
     * @param board: A list of lists of character
     * @param words: A list of string
     * @return: A list of string
     *          we will sort your return value in output
     */
    vector<string> wordSearchII(vector<vector<char>> &board, vector<string> &words) {
        int nRow = board.size();
        int nCol = board[0].size();
        root = new TrieNode();
        visited.resize(nRow, vector<bool>(nCol, false));
        for (auto word : words) insertWord(word);
        unordered_set<string> res;
        for (int i = 0; i < nRow; i++) {
            for (int j = 0; j < nCol; j++) {
                string str = string(1, board[i][j]);
                if (startWith(str)) {
                    visited[i][j] = true;
                    string str = string(1, board[i][j]);
                    helper(board, words, i, j, str, res, visited);
                    visited[i][j] = false;
                }
            }
        }
        vector<string> ret(res.begin(), res.end());
        return ret;
    }
private:
    TrieNode *root;
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    vector<vector<bool>> visited;
    void helper(vector<vector<char>> &board, vector<string> &words, int x, int y, 
                string str, unordered_set<string> &res, vector<vector<bool>> &visited) {
        if (searchWord(str)) {
            res.insert(str);
        }
        for (int i = 0; i < 4; i++) {
            int newX = x + dx[i];
            int newY = y + dy[i];
            if (newX >= 0 && newX < board.size() && newY >= 0 && newY < board[0].size()
                && !visited[newX][newY]) {
                str += board[newX][newY];
                if (startWith(str)) {
                    visited[newX][newY] = true;
                    helper(board, words, newX, newY, str, res, visited);
                    visited[newX][newY] = false;
                }
                str.pop_back();
            }
        }
    }
    void insertWord(string& word) {
        TrieNode *cur = root;
        int n = word.size();
        for (int i = 0; i < n; i++) {
            if (!cur->children[word[i] - 'a']) cur->children[word[i] - 'a'] = new TrieNode();
            cur = cur->children[word[i] - 'a'];
        }
        cur->isWord = true;
    }
    bool searchWord(string &str) {
        TrieNode *cur = root;
        int n = str.size();
        for (int i = 0; i < n; i++) {
            if (!cur->children[str[i] - 'a']) return false;
            cur = cur->children[str[i] - 'a'];
        }
        if (cur->isWord) return true;
        return false;
    }
    bool startWith(string &str) {
        TrieNode *cur = root;
        int n = str.size();
        for (int i = 0; i < n; i++) {
            if (!cur->children[str[i] - 'a']) return false;
            cur = cur->children[str[i] - 'a'];
        }
        return true;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值