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

文章讨论了一种解决LeetCode上的单词搜索II问题的算法,该算法首先构建字典树存储所有候选单词,然后对每个棋盘位置进行DFS搜索,通过字典树进行剪枝优化,以避免超时。虽然算法在某些情况下接近时间限制,但最终通过了测试。
摘要由CSDN通过智能技术生成

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

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

示例 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:
在这里插入图片描述

输入: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<vector>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<iostream>

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值