【回溯 字典樹(前綴樹)】212. 單詞搜索 II

本文涉及知識點

回溯 字典樹(前綴樹)

LeetCode212. 單詞搜索 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 * 104
1 <= words[i].length <= 10
words[i] 由小寫英文字母組成
words 中的所有字符串互不相同

回溯

用字典樹存儲所有words。
枚舉起點,時間復雜度O(nm)。
最長的words[i],除去第一個字符后,最多9個字符。后續字符4個。故處理每個起點的時間復雜是O(9n)
總時間復雜度 ≈ \approx 3$\times$107 超時邊緣。
回溯部分一定要反復優化。

代碼

核心代碼

struct CVec
{int r;int c;CVec operator*(const int len)const{return { r * len,c * len };}
};struct CPos
{	int r = 0, c = 0;CPos(int tr, int tc) {r = tr;c = tc;}int ToMask()const { return s_MaxC * r + c; };bool operator==(const CPos& o)const{return (r == o.r) && (c == o.c);}CPos operator+(const CVec& v)const{return { r + v.r, c + v.c };}CPos operator-(const CVec& v)const{return{ r - v.r, c - v.c };}CVec operator-(const CPos& o)const{return {r - o.r,c- o.c};}inline static  int s_MaxC = 10'000;
};struct SPosHash {// 哈希函數的操作符重載,接受一個指針作為參數size_t operator()(const CPos& pos) const {// 簡單的哈希函數示例,將指針地址轉換為哈希值return (long long)100'000 * pos.r + pos.c;;}
};class CRange
{
public:CRange(int rCount, int cCount, std::function<bool(int, int)> funVilidCur):m_r(rCount),m_c(cCount), m_funVilidCur(funVilidCur){}bool Vilid(CPos pos)const{return (pos.r >= 0) && (pos.r < m_r) && (pos.c >= 0) && (pos.c < m_c) && m_funVilidCur(pos.r, pos.c);}const int m_r, m_c;
protected:std::function<bool(int, int)> m_funVilidCur;
};template<class TData = char, int iTypeNum = 26, TData cBegin = 'a'>
class CTrieNode
{
public:CTrieNode* AddChar(TData ele, int& iMaxID){
#ifdef _DEBUGif ((ele < cBegin) || (ele >= cBegin + iTypeNum)){return nullptr;}
#endifconst int index = ele - cBegin;auto ptr = m_vPChilds[ele - cBegin];if (!ptr){m_vPChilds[index] = new CTrieNode();
#ifdef _DEBUGm_vPChilds[index]->m_iID = ++iMaxID;m_childForDebug[ele] = m_vPChilds[index];
#endif}return m_vPChilds[index];}CTrieNode* GetChild(TData ele)const{
#ifdef _DEBUGif ((ele < cBegin) || (ele >= cBegin + iTypeNum)){return nullptr;}
#endifreturn m_vPChilds[ele - cBegin];}
protected:
#ifdef _DEBUGint m_iID = -1;std::unordered_map<TData, CTrieNode*> m_childForDebug;
#endif
public:int m_iLeafIndex = -1;
protected:CTrieNode* m_vPChilds[iTypeNum] = { nullptr };
};template<class TData = char, int iTypeNum = 26, TData cBegin = 'a'>
class CTrie
{
public:int GetLeadCount(){return m_iLeafCount;}CTrieNode<TData, iTypeNum, cBegin>* AddA(CTrieNode<TData, iTypeNum, cBegin>* par,TData curValue){auto curNode =par->AddChar(curValue, m_iMaxID);FreshLeafIndex(curNode);return curNode;}template<class IT>int Add(IT begin, IT end){auto pNode = &m_root;for (; begin != end; ++begin){pNode = pNode->AddChar(*begin, m_iMaxID);}FreshLeafIndex(pNode);return pNode->m_iLeafIndex;}	template<class IT>CTrieNode<TData, iTypeNum, cBegin>* Search(IT begin, IT end){auto ptr = &m_root;for (; begin != end; ++begin){ptr = ptr->GetChild(*begin);if (nullptr == ptr){return nullptr;}}return ptr;}CTrieNode<TData, iTypeNum, cBegin> m_root;
protected:void FreshLeafIndex(CTrieNode<TData, iTypeNum, cBegin>* pNode){if (-1 == pNode->m_iLeafIndex){pNode->m_iLeafIndex = m_iLeafCount++;}}int m_iMaxID = 0;int m_iLeafCount = 0;
};class Solution {
public:vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {const int R = board.size();const int C = board[0].size();for (const auto& s : words) {m_trie.Add(s.begin(), s.end());}		vector<bool> vHas(m_trie.GetLeadCount());int hasVisit[12][12] ;memset(hasVisit, 0, sizeof(hasVisit));CRange rang(R, C, [&](int r, int c) {return !hasVisit[r][c]; });int move[][2] = { {0,1},{0,-1},{1,0},{-1,0}};std::function<void(CTrieNode<>* par, int r, int c)> BackTrack = [&](CTrieNode<>* par, int r, int c){auto p = par->GetChild(board[r][c]);if (nullptr == p) { return; }if (-1 != p->m_iLeafIndex) {vHas[p->m_iLeafIndex] = true;}hasVisit[r][c]=true;for (int i = 0; i < 4; i++) {const int r1 = r + move[i][0];const int c1 = c + move[i][1];if(!rang.Vilid(CPos(r1,c1 ))){continue;}BackTrack(p, r1, c1);}hasVisit[r][c] = false;};for (int r = 0; r < R; r++) {for (int c = 0; c < C; c++) {BackTrack(&m_trie.m_root, r, c);}}vector<string> vRet;for (const auto& s : words) {auto p = m_trie.Search(s.begin(), s.end());if (vHas[p->m_iLeafIndex]) {vRet.emplace_back(s);}}return vRet;}	CTrie<> m_trie;
};

測試用例

template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){assert(v1[i] == v2[i]);}
}template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}int main()
{vector<vector<char>> board;vector<string> words;{Solution slu;board= { {'o','a','a','n'},{'e','t','a','e'},{'i','h','k','r'},{'i','f','l','v'} },words= { "oath","pea","eat","rain" };auto res = slu.findWords(board, words);Assert({ "oath","eat" }, res);}{Solution slu;board = { {'a','b'},{'c','d'} }, words = { "abcb" };auto res = slu.findWords(board, words);Assert({  }, res);}	}

2023年4月版

class Solution {
public:vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {m_r = board.size();m_c = board[0].size();m_iMaskNum = m_r*m_c;m_vMove.resize(m_iMaskNum);for (int r = 0; r < m_r; r++){for (int c = 0; c < m_c; c++){int iMask = m_c*r + c;if (r > 0){m_vMove[iMask].emplace_back(iMask - m_c);}if (r + 1 < m_r){m_vMove[iMask].emplace_back(iMask + m_c);}if ( c > 0 ){m_vMove[iMask].emplace_back(iMask - 1);}if (c + 1 < m_c){m_vMove[iMask].emplace_back(iMask + 1);}}}for (const auto& s : words){long long llRet = 0;for (const auto& ch : s){llRet = llRet * 27 + ch - 'a'+1;m_setNeedSub.emplace(llRet);}m_setNeed.emplace(llRet);}int v[12] = { 0 };bool bPre[12 * 12] = { 0 };for (int i = 0; i < m_iMaskNum; i++){			const char& ch = board[i / m_c][i%m_c];v[0] = i;bPre[i] = true;dfs(v, 1,bPre, ch - 'a' + 1, board);bPre[i] = false;}vector<string> vRet;for (const auto& s : words){const long long llMask = Mask(s);if (!m_setNeed.count(llMask)){vRet.emplace_back(s);}}return vRet;}void dfs(int* v,int iSize, bool* vPre, const long long llMask, vector<vector<char>>& board){if (iSize > 10){return;}if (m_setNeed.count(llMask)){m_setNeed.erase(llMask);auto tmp = llMask;while (tmp > 0){auto it = m_setNeedSub.find(tmp);m_setNeedSub.erase(it);tmp /= 27;}}if (!m_setNeedSub.count(llMask)){return;}//const int iSize = v.size();const int iCur = v[iSize-1];for (const auto& next : m_vMove[iCur]){if (vPre[next]){continue;}v[iSize] = next;vPre[next] = true;dfs(v, iSize+1,vPre, llMask * 27 + board[next / m_c][next%m_c] - 'a' + 1, board);vPre[next] = false;}}long long Mask(const std::string& str){long long llRet = 0;for (const auto& ch : str){llRet = llRet * 27 + ch - 'a'+1;}return llRet;}int m_r, m_c;int m_iMaskNum;vector<vector<int>> m_vMove;std::unordered_multiset<long long> m_setNeedSub;std::unordered_set<long long> m_setNeed;
};

擴展閱讀

視頻課程

有效學習:明確的目標 及時的反饋 拉伸區(難度合適),可以先學簡單的課程,請移步CSDN學院,聽白銀講師(也就是鄙人)的講解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成戰斗了,為老板分憂,請學習C#入職培訓、C++入職培訓等課程
https://edu.csdn.net/lecturer/6176

相關下載

想高屋建瓴的學習算法,請下載《喜缺全書算法冊》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想對大家說的話
《喜缺全書算法冊》以原理、正確性證明、總結為主。
聞缺陷則喜是一個美好的愿望,早發現問題,早修改問題,給老板節約錢。
子墨子言之:事無終始,無務多業。也就是我們常說的專業的人做專業的事。
如果程序是一條龍,那算法就是他的是睛

測試環境

操作系統:win7 開發環境: VS2019 C++17
或者 操作系統:win10 開發環境: VS2022 C++17
如無特殊說明,本算法用**C++**實現。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/10782.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/10782.shtml
英文地址,請注明出處:http://en.pswp.cn/web/10782.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第3周 后端微服務基礎架構與前端項目聯調配備

第3周 后端微服務基礎架構與前端項目聯調配備 1. 微服務項目層次設計與Maven聚合1.1 項目層次設計1.2 父項目pom1.2.1 打包方式 1.3 創建通用 ************************************************************************************** 1. 微服務項目層次設計與Maven聚合 1.1…

電商平臺遭遇DDOS、CC攻擊有什么防護方案

電商平臺遭遇DDOS、CC攻擊有什么防護方案&#xff1f;在數字化浪潮的推動下&#xff0c;電商平臺已成為現代商業的重要組成部分&#xff0c;為消費者提供便捷、多樣的購物體驗。然而&#xff0c;隨著業務的發展&#xff0c;電商平臺也面臨著日益嚴峻的網絡安全挑戰&#xff0c;…

Tower for Mac:Git管理的新境界

Tower for Mac&#xff0c;讓您的Git管理進入新境界&#xff01;這款專為Mac用戶打造的Git客戶端&#xff0c;憑借其出色的性能和豐富的功能&#xff0c;成為眾多開發者的首選工具。 Tower不僅支持常規的Git操作&#xff0c;如提交、推送和拉取&#xff0c;還提供了許多高級功能…

四、VGA項目:聯合精簡幀+雙fifo+sobel算法 實現VGA顯示

前言&#xff1a;該項目實際上是在很多基礎的小練習上合成起來的&#xff0c;例如涉及到uart&#xff08;rs232&#xff09;的數據傳輸、雙fifo流水線操作、VGA圖像顯示&#xff0c;本次內容在此基礎上又增添了sobel算法&#xff0c;能實現圖像的邊沿監測并VGA顯示。 文章目錄…

簡單的DbUtils工具類【精細】

目錄 單條通用增刪改方法 1.創建maven項目&#xff0c;并加載依賴 2.創建數據庫連接工具類(Dbutils類) 3.創建一個執行器(SqlExecutor類) 4.通用(增&#xff0c;刪&#xff0c;改)方法 1.創建方法 2.創建userInfo實體類 3.創建測試類&#xff0c;測試增&#xff0c;刪&#xf…

探索數據結構:樹與二叉樹

?? 歡迎大家來到貝蒂大講堂?? &#x1f388;&#x1f388;養成好習慣&#xff0c;先贊后看哦~&#x1f388;&#x1f388; 所屬專欄&#xff1a;數據結構與算法 貝蒂的主頁&#xff1a;Betty’s blog 1. 樹 1.1. 樹的定義 樹是一種非線性的數據結構&#xff0c;它是由n&a…

ORA-609頻繁出現在alert.log,如何解決?

ORA-609就alertlog中比較常見的一個報錯&#xff0c;雖然并沒有太大的影響&#xff0c;但是頻繁的出現在alert log也是很讓人厭煩的事情&#xff0c;本文介紹如何排查解決ORA-609問題。 1.ORA-609官方定義 could not attach to incoming connection Cause Oracle process cou…

【SRC實戰】前端脫敏信息泄露

挖個洞先 https://mp.weixin.qq.com/s/xnCQQCAneT21vYH8Q3OCpw “ 以下漏洞均為實驗靶場&#xff0c;如有雷同&#xff0c;純屬巧合 ” 01 — 漏洞證明 一、前端脫敏&#xff0c;請求包泄露明文 “ 前端脫敏處理&#xff0c;請求包是否存在泄露&#xff1f; ” 1、獲取驗…

|Python新手小白中級教程|第二十八章:面向對象編程(類定義語法私有屬性類的繼承與多態)(4)

文章目錄 前言一、類定義語法二、私有方法和私有屬性1.私有屬性2.私有方法 三、類“繼承”1.初識繼承2.使用super函數調用父類中構造的東西 四、類“多態”1.多態基礎2.子類不同形態3.使用isinstance函數與多態結合判斷類型 總結 前言 大家好&#xff0c;我是BoBo仔吖&#xf…

6818Linux內核開發移植

Linux內核開發移植 Linux內核版本變遷及其獲得 Linux是最受歡迎的自由電腦操作系統內核&#xff0c; 是一個用C語言寫成&#xff0c; 并且符合POSIX標準的類Unix操作系統 Linux是由芬蘭黑客Linus Torvalds開發的&#xff0c; 目的是嘗試在英特爾x86架構上提供自由免費的類Un…

Task Office for Mac v9.0激活版:任務管理新境界

還在為繁瑣的任務管理而煩惱嗎&#xff1f;Task Office for Mac為您帶來全新的任務管理體驗。簡潔明了的界面設計&#xff0c;讓您輕松上手&#xff1b;強大的任務管理和項目管理功能&#xff0c;讓您輕松掌握任務進度&#xff1b;多用戶協作功能&#xff0c;讓團隊協作更加高效…

ubuntu24.04安裝ros

ubuntu24.04安裝ros 踩坑 踩坑 目前安裝人數比較少&#xff0c;沒有較為詳細的博客&#xff0c;參考官網的鏈接 http://docs.ros.org/en/rolling/Installation/Ubuntu-Install-Debians.html 同時在如下的一步中會找不到網址報錯&#xff0c;此時可以參考https://blog.51cto.c…

Excel辦公技巧之下拉菜單

在日常辦工中&#xff0c;經常需在單元格中輸入特定的值&#xff0c;此時我們可以使用下拉菜單解決&#xff0c;輸入錯誤和錯誤值&#xff0c;可以一勞永逸的解決固定數據輸入問題。 使用Excel下拉菜單時&#xff0c;它在數據輸入和驗證方面發揮著重要作用通過點擊單元格的下拉…

學習筆記-Vue3中Hook函數

什么是Hook函數 Hook翻譯過來是鉤子的意思&#xff0c;其本質上是一組可復用的函數。簡單理解來說&#xff0c;你能夠在不同的組件中&#xff0c;實現相同的代碼邏輯&#xff0c;以達到代碼復用、提高維護性的效果。那為何叫’鉤子’呢&#xff0c;我的理解是&#xff1a; 它可…

商業數據分析--時間序列圖及趨勢分析

繪制時間序列圖,并指出存在什么樣的狀態如上兩圖: 可見狀態:從時間序列圖可以看出,這些數據存在明顯的季節性波動,每年的第4季度值都最高,而第2季度值最低。同時也存在一些下降的趨勢。 通過引進虛擬變量,建立多元線性回歸模型。答: 通過引入虛擬變量,我們可以建立如下的…

Oracle數據庫之多表查詢、層次查詢(五)

目錄 前言 Oracle 的連接條件的類型 多表查詢 1. 使用JOIN關鍵字 2. 使用WHERE子句進行多表查詢 3. 子查詢 4. EXISTS關鍵字 5. 集合運算 6. 注意事項&#xff1a; 層次查詢 前言 Oracle 的連接條件的類型 等值連接不等值連接外連接自連接 多表查詢 在Oracle數據…

商場學習之微服務

前言 寒假前在新電腦上配置了java環境&#xff0c;maven倉庫&#xff0c;node,js&#xff0c;navicat&#xff0c;MySQL&#xff0c;linux&#xff0c;vmware等環境&#xff0c;創建了6個mysql數據庫&#xff0c;77張表。 如此多的表&#xff0c;字段&#xff0c;去手寫基礎…

Web入門——三欄布局頁面

前置知識 內外邊距 內邊距(padding)&#xff1a; padding是元素邊框與其內容之間的空間。也就是說&#xff0c;如果你給一個元素設置了內邊距&#xff0c;這個空間會作為元素內容與元素邊框之間的緩沖區域。設置內邊距會使元素本身變大。例如padding:10px就創建了10像素的空間…

Qt之QMqtt 發送圖片數據

簡述 MQTT(消息隊列遙測傳輸)是ISO標準下基于發布/訂閱范式的消息協議;它工作在TCP/IP協議族上,是為硬件性能低下的遠程設備以及網絡狀況糟糕的情況下而設計的發布/訂閱型消息協議,為此,它需要一個消息中間件; MQTT是一個基于客戶端-服務器的消息發布/訂閱傳輸協議;MQT…

Ubuntu設置中午輸入法

本篇博客將指導您如何在Ubuntu系統中設置中文輸入法&#xff0c;讓您能夠輕松地進行中文輸入。 準備工作 在開始之前&#xff0c;請確保您的系統已經更新到最新版本。這可以通過以下命令來完成&#xff1a; sudo apt update && sudo apt upgrade這將幫助確保所有的軟…