LeetCode 212. 单词搜索 II 【字典树+回溯】

题目链接:https://leetcode.cn/problems/word-search-ii/
给定一个 m x n 二维字符网格 board 和一个单词(字符串)列表 words, 返回所有二维网格上的单词 。

单词必须按照字母顺序,通过 相邻的单元格 内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

示例 1:
LeetCode 212. 单词搜索 II 【字典树+回溯】_第1张图片

输入:board = [[“o”,“a”,“a”,“n”],[“e”,“t”,“a”,“e”],[“i”,“h”,“k”,“r”],[“i”,“f”,“l”,“v”]], words = [“oath”,“pea”,“eat”,“rain”]
输出:[“eat”,“oath”]
示例 2:
LeetCode 212. 单词搜索 II 【字典树+回溯】_第2张图片

输入:board = [[“a”,“b”],[“c”,“d”]], words = [“abcb”]
输出:[]

提示:

m == board.length
n == board[i].length
1 <= m, n <= 12
board[i][j] 是一个小写英文字母
1 <= words.length <= 3 * 10^4
1 <= words[i].length <= 10
words[i] 由小写英文字母组成
words 中的所有字符串互不相同

解题思路:
因为棋盘不大,但是单词很多,所以不能一个一个枚举单词是否在棋盘中是否存在,这样要超时

可以先利用字典树,把所有的候选单词存入字典树,这样就只需要枚举每一个棋盘的位置做起点,利用DFS搜索以该路径出发,能够构造哪些单词。

注意搜索的时候,可以进行如下剪枝:当前位置的上下左右4个方向,并不是都可以作为候选落点,要根据字典树的提示,过滤到那些明显不会有候选词的路径。

最后,看起来我的算法问题应该不大,但是提交的时候超时了很多次,最后没有办法,把字典树当中有个本来通过递归来查询当前节点单词的方法改成了用缓存直接记答案。我不知道是不是我用了太多STL和开辟内存空间等操作。最后擦线过的,代码做个参考吧

#include
#include
#include
#include
#include
#include

using namespace std;

#define MAX_KEY 26
#define MAX_N 13

struct TrieNode{
    TrieNode* father;
    TrieNode* next[MAX_KEY];
    set<char> key_set;
    char data;
    string word;
    TrieNode()
    {
        this->data='\0';
        this->word="";
        this->father=NULL;
        for(int i=0;i<MAX_KEY;++i)
            this->next[i]=NULL;
    }
};

struct Point{
    int x;
    int y;
    TrieNode* node;
    Point(){};
    Point(int x,int y)
    {
        this->x=x;
        this->y=y;
        this->node=NULL;
    }
};

class Trie{
private:
    TrieNode* root;
public:
    TrieNode* get_root()
    {
        return root;
    }

    void init()
    {
        this->root=new TrieNode();
    }

    void insert_word(string word)
    {
        TrieNode* now_root=root;
        for(int i=0;i<word.size();++i)
        {
            char c=word[i];
            int ci=c-'a';
            if(now_root->key_set.find(c)!=now_root->key_set.end())
            {
                now_root=now_root->next[ci];
            }
            else
            {
                now_root->key_set.insert(c);
                now_root->next[ci]=new TrieNode();
                now_root->next[ci]->data=c;
                now_root->next[ci]->father=now_root;
                now_root->next[ci]->word=now_root->word+c;
                now_root=now_root->next[ci];
            }
        }
    }

    // 本来这里是用递归实现的,但是用了要超时
    string query_word(TrieNode* child)
    {
        return child->word;
    }
};

class Solution{
private:
    int n,m;
    vector<Point> start_pos;
    vector<vector<char>> board;
    vector<string> words;
    Trie trie;
    set<string> word_set;
    int dx[4]={1,-1,0,0};
    int dy[4]={0,0,1,-1};
    bool flag[MAX_N][MAX_N];

    void init(vector<vector<char>>& board, vector<string>& words)
    {
        this->m=board.size();
        this->n=board[0].size();
        this->trie.init();
        this->board=board;
        this->words=words;
        set<char> start_char;
        this->start_pos.clear();
        for(int i=0;i<words.size();++i)
        {
            this->trie.insert_word(words[i]);
            start_char.insert(words[i][0]);
            this->word_set.insert(words[i]);
        }
        for(int i=0;i<board.size();++i)
        {
            for(int j=0;j<board[i].size();++j)
            {
                if(start_char.find(board[i][j])!=start_char.end())
                {
                    Point pt(i,j);
                    this->start_pos.push_back(pt);
                }
            }
        }
    }

    void fill_start_point(Point& start_pos,vector<vector<char>>& board)
    {
        int x=start_pos.x;
        int y=start_pos.y;
        int c=board[x][y]-'a';
        start_pos.node=this->trie.get_root()->next[c];
    }

    void deep_first_search(Point& pos,vector<string>& result)
    {
        string word=this->trie.query_word(pos.node);
        if(this->word_set.find(word)!=this->word_set.end())
        {
            result.push_back(word);
            this->word_set.erase(word);
        }
        for(int i=0;i<4;++i)
        {
            int nx=pos.x+dx[i];
            int ny=pos.y+dy[i];
            if(nx>=0 && nx<this->m && ny>=0 && ny<this->n && !flag[nx][ny])
            {
                char c=board[nx][ny];
                int ci=c-'a';
                if(pos.node->key_set.find(c)!=pos.node->key_set.end())
                {
                    flag[nx][ny]=true;
                    Point next_pos(nx,ny);
                    next_pos.node=pos.node->next[ci];
                    deep_first_search(next_pos,result);
                    flag[nx][ny]=false;
                }
            }
        }
    }
public:
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words)
    {
        vector<string> result;
        this->init(board,words);
        for(int i=0;i<this->start_pos.size();++i)
        {
            if(this->word_set.empty())
                break;
            Point st=start_pos[i];
            memset(this->flag,0,sizeof(this->flag));
            this->flag[st.x][st.y]=true;
            this->fill_start_point(st,board);
            this->deep_first_search(st,result);
        }
        return result;
    }
};

int main()
{
    Solution solve;
    vector<vector<char>> board={{'o','a','a','n'},{'e','t','a','e'},{'i','h','k','r'},{'i','f','l','v'}};
    vector<string> words={"oath","pea","eat","rain","hklf", "hf","oaaneateihkrvlfi"};
    vector<string> result=solve.findWords(board,words);
    for(int i=0;i<result.size();++i)
        cout<<result[i]<<endl;
    return 0;
}

你可能感兴趣的:(回溯,字典树,leetcode,算法)