力扣212. 单词搜索 II

博客围绕在二维网格中查找字典单词的问题展开。给定二维网格和单词列表,需找出同时出现的单词,单词由相邻单元格字母构成且同一单元格字母不重复使用。介绍了优化回溯算法,将单词插入前缀树,通过动态规划查询,查到的结果要去重。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:
给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。

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

示例:

输入:
words = [“oath”,“pea”,“eat”,“rain”] and board =
[
[‘o’,‘a’,‘a’,‘n’],
[‘e’,‘t’,‘a’,‘e’],
[‘i’,‘h’,‘k’,‘r’],
[‘i’,‘f’,‘l’,‘v’]
]
输出: [“eat”,“oath”]

说明:
你可以假设所有输入都由小写字母 a-z 组成。

提示:
你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯?
如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这篇文章

思路
先将所有words插入前缀树;
对二维网格 board 每一个位置作为首字母开始动态规划查询当前单词是否在前缀树中,如果前缀树中有以该单词开头的则在可移动的上下左右范围内继续动态查找,如果前缀树中查到该单词则直接加入最后要返回的查到的单词的vector中,若在前缀树中查询不到以该单词开头的词则立即停止回溯,继续下一个位置为首字母的查找;
最后注意所有查到的单词的vector要去重。

vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        wordslist.clear();
        a = board;
        int n = board.size();
        int m = board[0].size();
        used.resize(n, vector<bool>(m,false));
        if(n > 0 && m > 0 && words.size() > 0) {
            Trie* T = new Trie();
            // 插入前缀树
            for(int k = 0; k < words.size(); k++) {
                T->insert(words[k]);
            }
            // 对二维网格 board 每一个位置作为首字母开始动态规划查询
            for(int i = 0; i < board.size(); i++) {
                for(int j = 0; j < board[0].size(); j++) {
                    findchars(i, j, T, "");
                }
            }
            delete T;
        }
        // 去重,unique()函数将重复的元素放到vector的尾部 然后返回指向第一个重复元素的迭代器 再用erase函数擦除从这个元素到最后元素的所有的元素
        sort(wordslist.begin(),wordslist.end());
        wordslist.erase(unique(wordslist.begin(), wordslist.end()), wordslist.end());
        
        return wordslist;
    }

所有代码:

class Solution {
public:
    class TrieNode {
    public:
        TrieNode* children[26];
        string item = "";
    };

    class Trie {
    private:
        TrieNode* root;
    public:
        /** Initialize your data structure here. */
        Trie() {
            root = new TrieNode();
        }

        /** Inserts a word into the trie. */
        void insert(string word) {
            TrieNode* node = this->root;
            for(int i = 0; i < word.length(); i++) {
                if(node->children[word[i]-'a'] == NULL) {
                    node->children[word[i]-'a'] = new TrieNode();
                }
                node = node->children[word[i]-'a'];
            }
            node->item = word;
        }

        /** Returns if the word is in the trie. */
        bool search(string word) {
            TrieNode* node = this->root;
            for(int i = 0; i < word.length(); i++) {
                if(node->children[word[i]-'a'] == NULL) {
                    return false;
                }
                node = node->children[word[i]-'a'];
            }
            return node->item == word;
        }

        /** Returns if there is any word in the trie that starts with the given prefix. */
        bool startsWith(string prefix) {
            TrieNode* node = this->root;
            for(int i = 0; i < prefix.length(); i++) {
                if(node->children[prefix[i]-'a'] == NULL) {
                    return false;
                }
                node = node->children[prefix[i]-'a'];
            }
            return true;
        }
    };
    
    vector<string> wordslist;
    vector<vector<bool>> used;
    vector<vector<char>> a;
    
    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        wordslist.clear();
        a = board;
        int n = board.size();
        int m = board[0].size();
        used.resize(n, vector<bool>(m,false));
        if(n > 0 && m > 0 && words.size() > 0) {
            Trie* T = new Trie();
             // 插入前缀树
            for(int k = 0; k < words.size(); k++) {
                T->insert(words[k]);
            }
             // 对二维网格 board 每一个位置作为首字母开始动态规划查询
            for(int i = 0; i < board.size(); i++) {
                for(int j = 0; j < board[0].size(); j++) {
                    findchars(i, j, T, "");
                }
            }
            delete T;
        }
        //去重,unique()函数将重复的元素放到vector的尾部 然后返回指向第一个重复元素的迭代器 再用erase函数擦除从这个元素到最后元素的所有的元素
        sort(wordslist.begin(),wordslist.end());
        wordslist.erase(unique(wordslist.begin(), wordslist.end()), wordslist.end());
        
        return wordslist;
    }
    
    // 动态规划查询部分
    void findchars(int i, int j, Trie* t, string curstr) {
        curstr += a[i][j];
        used[i][j] = true;
        if(t->search(curstr)) {
            wordslist.push_back(curstr);
        }
        if(t->startsWith(curstr)) {
            if(i > 0 && used[i-1][j] != true) {
                findchars(i-1, j, t, curstr);
            }
            if(i < a.size()-1 && used[i+1][j] != true) {
                findchars(i+1, j, t, curstr);
            }
            if(j > 0 && used[i][j-1] != true) {
                findchars(i, j-1, t, curstr);
            }
            if(j < a[0].size()-1 && used[i][j+1] != true) {
                findchars(i, j+1, t, curstr);
            }
        }
        used[i][j] = false;
        return;
    }
};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值