676. 實(shí)現(xiàn)一個(gè)魔法字典 : 結(jié)合 DFS 的 Trie 運(yùn)用題

題目描述

這是 LeetCode 上的 676. 實(shí)現(xiàn)一個(gè)魔法字典 ,難度為 中等函筋。

Tag : 「字典樹」沙合、「DFS」

設(shè)計(jì)一個(gè)使用單詞列表進(jìn)行初始化的數(shù)據(jù)結(jié)構(gòu),單詞列表中的單詞 互不相同 驻呐。 如果給出一個(gè)單詞灌诅,請(qǐng)判定能否只將這個(gè)單詞中一個(gè)字母換成另一個(gè)字母,使得所形成的新單詞存在于你構(gòu)建的字典中含末。

實(shí)現(xiàn) MagicDictionary 類:

  • MagicDictionary() 初始化對(duì)象
  • void buildDict(String[] dictionary) 使用字符串?dāng)?shù)組 dictionary 設(shè)定該數(shù)據(jù)結(jié)構(gòu)猜拾,dictionary 中的字符串互不相同
  • bool search(String searchWord) 給定一個(gè)字符串 searchWord,判定能否只將字符串中 一個(gè) 字母換成另一個(gè)字母佣盒,使得所形成的新字符串能夠與字典中的任一字符串匹配挎袜。如果可以,返回 true肥惭;否則盯仪,返回 false

示例:

輸入
["MagicDictionary", "buildDict", "search", "search", "search", "search"]
[[], [["hello", "leetcode"]], ["hello"], ["hhllo"], ["hell"], ["leetcoded"]]

輸出
[null, null, false, true, false, false]

解釋
MagicDictionary magicDictionary = new MagicDictionary();
magicDictionary.buildDict(["hello", "leetcode"]);
magicDictionary.search("hello"); // 返回 False
magicDictionary.search("hhllo"); // 將第二個(gè) 'h' 替換為 'e' 可以匹配 "hello" 蜜葱,所以返回 True
magicDictionary.search("hell"); // 返回 False
magicDictionary.search("leetcoded"); // 返回 False

提示:

  • 1 <= dictionary.length <= 100
  • 1 <= dictionary[i].length <= 100
  • dictionary[i] 僅由小寫英文字母組成
  • dictionary 中的所有字符串 互不相同
  • 1 <= searchWord.length <= 100
  • searchWord 僅由小寫英文字母組成
  • buildDict 僅在 search 之前調(diào)用一次
  • 最多調(diào)用 100search

Trie + DFS

為了方便全景,我們令 dictionaryss,令 searchWords牵囤。

整體題意:給定字符串 s爸黄,問能否存在替換掉 s 中的某個(gè)字符滞伟,使得新字符串出現(xiàn)在 ss 數(shù)組中。

考慮如何使用「字典樹/Trie」求解該問題:

  • buildDict 操作:我們可以將所有的 ss[i] 存入字典樹中炕贵,方便后續(xù)檢索梆奈;

  • search 操作:設(shè)計(jì)遞歸函數(shù) boolean query(String s, int idx, int p, int limit),其中 s 為待檢索的字符串称开,idx 為當(dāng)前處理到字符串 s 的哪一位亩钟,p 為當(dāng)前搜索到字典樹的索引編號(hào)(起始有 p = 0),limit 為當(dāng)前剩余的替換字符次數(shù)鳖轰,根據(jù)題意清酥,limit 固定為 1,含義為必須替換掉 s 的一個(gè)字符脆霎。
    對(duì)于 s[idx] 而言总处,我們可以枚舉新字符串在當(dāng)前位置是何種字符(C = 26 個(gè)選擇),若當(dāng)前枚舉到的字符與 s[idx] 一致睛蛛,則不消耗替換次數(shù)鹦马。
    爆搜過程中替換次數(shù)為負(fù)數(shù)直接剪枝,當(dāng)爆搜到結(jié)尾位置忆肾,再檢查當(dāng)前的字典樹索引 p 是否為單詞結(jié)尾節(jié)點(diǎn)(對(duì)應(yīng)查詢數(shù)組 ss 中是否存在該字符串)荸频,以及剩余的替換次數(shù) limit 是否為 0

不了解「Trie / 字典樹」的同學(xué)可以看前置 ??:字典樹入門客冈。里面通過圖例展示了字典樹基本形態(tài)旭从,以及提供了「數(shù)組實(shí)現(xiàn)」和「TrieNode 實(shí)現(xiàn)」兩種方式,還有「數(shù)組大小估算方式」和「Trie 應(yīng)用面」介紹

代碼:

class MagicDictionary {
    int N = 100 * 100, M = 26, idx = 0;
    int[][] tr = new int[N][M];
    boolean[] isEnd = new boolean[N * M];
    void add(String s) {
        int p = 0;
        for (int i = 0; i < s.length(); i++) {
            int u = s.charAt(i) - 'a';
            if (tr[p][u] == 0) tr[p][u] = ++idx;
            p = tr[p][u];
        }
        isEnd[p] = true;
    }
    boolean query(String s, int idx, int p, int limit) {
        if (limit < 0) return false;
        if (idx == s.length()) return isEnd[p] && limit == 0;
        int u = s.charAt(idx) - 'a';
        for (int i = 0; i < 26; i++) {
            if (tr[p][i] == 0) continue;
            if (query(s, idx + 1, tr[p][i], i == u ? limit : limit - 1)) return true;
        }
        return false;
    }
    public void buildDict(String[] ss) {
        for (String s : ss) add(s);
    }
    public boolean search(String s) {
        return query(s, 0, 0, 1);
    }
}
  • 時(shí)間復(fù)雜度:buildDict 操作需要將所有字符存入 Trie场仲,復(fù)雜度為 \sum_{i = 0}^{n - 1} len(ss[i]])和悦;search 操作在不考慮 limit 以及字典樹中最多只有 100 具體方案所帶來的剪枝效果的話,最壞情況下要搜索所有 C^L 個(gè)方案渠缕,其中 C = 26 為字符集大小鸽素,L = 100 為搜索字符串的最大長度
  • 空間復(fù)雜度:O(N \times L \times C),其中 N = 100 為存入 Trie 的最大方案數(shù)亦鳞,L = 100 為存入字符串的最大長度馍忽,C = 26 為字符集大小

最后

這是我們「刷穿 LeetCode」系列文章的第 No.676 篇,系列開始于 2021/01/01燕差,截止于起始日 LeetCode 上共有 1916 道題目遭笋,部分是有鎖題,我們將先把所有不帶鎖的題目刷完徒探。

在這個(gè)系列文章里面瓦呼,除了講解解題思路以外,還會(huì)盡可能給出最為簡潔的代碼测暗。如果涉及通解還會(huì)相應(yīng)的代碼模板央串。

為了方便各位同學(xué)能夠電腦上進(jìn)行調(diào)試和提交代碼谎替,我建立了相關(guān)的倉庫:https://github.com/SharingSource/LogicStack-LeetCode

在倉庫地址里蹋辅,你可以看到系列文章的題解鏈接、系列文章的相應(yīng)代碼挫掏、LeetCode 原題鏈接和其他優(yōu)選題解侦另。

更多更全更熱門的「筆試/面試」相關(guān)資料可訪問排版精美的 合集新基地 ????

本文由mdnice多平臺(tái)發(fā)布

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市尉共,隨后出現(xiàn)的幾起案子褒傅,更是在濱河造成了極大的恐慌,老刑警劉巖袄友,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件殿托,死亡現(xiàn)場離奇詭異,居然都是意外死亡剧蚣,警方通過查閱死者的電腦和手機(jī)支竹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸠按,“玉大人礼搁,你說我怎么就攤上這事∧考猓” “怎么了馒吴?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瑟曲。 經(jīng)常有香客問我饮戳,道長,這世上最難降的妖魔是什么洞拨? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任扯罐,我火速辦了婚禮,結(jié)果婚禮上扣甲,老公的妹妹穿的比我還像新娘篮赢。我一直安慰自己,他們只是感情好琉挖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布启泣。 她就那樣靜靜地躺著,像睡著了一般示辈。 火紅的嫁衣襯著肌膚如雪寥茫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天矾麻,我揣著相機(jī)與錄音纱耻,去河邊找鬼芭梯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛弄喘,可吹牛的內(nèi)容都是我干的玖喘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蘑志,長吁一口氣:“原來是場噩夢啊……” “哼累奈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起急但,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤澎媒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后波桩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體戒努,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年镐躲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了储玫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萤皂,死狀恐怖缘缚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情敌蚜,我是刑警寧澤桥滨,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站弛车,受9級(jí)特大地震影響齐媒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纷跛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一喻括、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贫奠,春花似錦唬血、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谢肾,卻和暖如春腕侄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工冕杠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留微姊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓分预,卻偏偏與公主長得像兢交,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子笼痹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容