手摸手提桶跑路——LeetCode720. 词典中最长的单词

150 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情

题目描述

给出一个字符串数组 words 组成的一本英语词典。返回 words 中最长的一个单词,该单词是由 words 词典中其他单词逐步添加一个字母组成。

若其中有多个可行的答案,则返回答案中字典序最小的单词。若无答案,则返回空字符串。

示例 1:

输入:words = ["w","wo","wor","worl", "world"]
输出:"world"
解释: 单词"world"可由"w", "wo", "wor", 和 "worl"逐步添加一个字母组成。

示例 2:

输入:words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
输出:"apple"
解释:"apply""apple" 都能由词典中的单词组成。但是 "apple" 的字典序小于 "apply" 

提示:

  • 1 <= words.length <= 1000
  • 1 <= words[i].length <= 30
  • 所有输入的字符串 words[i] 都只包含小写字母。

解题思路——Set遍历

题目中要求我们返回的最长单词必须是其他单词逐步拼成的单词。也就是说返回的这个长度为 n 的单词 word 必须有在词典中有其对应的 word.substring(0, 1)、word.substring(0,2)....word.substring(0, n -1 ),这个单词才能算数。

具体步骤如下:

  1. 初始化一个 Set
  2. 遍历 words, 将所有的单词都存入 Set 集合中。
  3. 初始化变量 maxWord='',用来记录最长的单词。
  4. 遍历 words 数组,对 word 的每个单词进行子串的截取,对于每个子串,去 Set 中查找有没有对应的数据,如果没找到则说明 word 不算数,没有必要继续遍历 word 了;如果有则继续查找下一个长度的子串,直到结束,说明该 word 是满足要求的,同 maxWord 比较,满足条件则 maxWord = word
  5. 返回 maxWord

这里可以对第二次 words 的遍历进行优化,比如说当我们的 maxWord = 'xxxxx',那么我的 maxWord 都这么长了,如果 maxWord 短的单词肯定就直接 pass 了。

题解

var longestWord = function(words) {
    const wordSet = new Set();

    for(const w of words) {
        wordSet.add(w);
    }

    let maxWord = '';
    for(const w of words) {
        const lens = w.length;
        if(maxWord.length > lens) continue; // 长度小于maxWord的就不用遍历了,减少范围
        let flag = false;
        for(let i=1; i<lens; ++i) {
            if(!wordSet.has(w.substring(0, i))) {
                flag = true;
                break;
            }
        }
        if(flag) continue;
        if(lens > maxWord.length) {
            maxWord = w;
        } else if(lens === maxWord.length) {
            if(w < maxWord) {
                maxWord = w;
            }
        }
    }
    return maxWord;
};

QQ截图20220817003418.png

优化

  1. 对 words 数组以 length 属性进行排序。
  2. 初始化一个 wordSet=new Set('')。
  3. 遍历 words,对于每个单词来说,如果 wordSet 中存在 word.slice(0, -1),也就是存在除了最后一个字符外的字符串的话,就可以把 word 添加进 wordSet,同时同 maxWord 进行比较。

["yo","ew","fc","zrc","yodn","fcm","qm","qmo","fcmz","z","ewq","yod","ewqz","y"] 这个数组来说,遍历到 ew 时,由于 wordSet 中不存在 e,就不能添加到 wordSet 集合中,那么连带的,ewqewqzslice(0, -1) 全部不存在。如此一来因为 words 按照 length 属性排序,我们只需要判断 slice(0, -1) 就可以了,极大的提高了效率。

var longestWord = function(words) {
    words.sort((a,b)=>a.length-b.length);

    let maxWord = '';
    const wordSet = new Set(['']);

    for(const w of words) {
        if(wordSet.has(w.slice(0, -1))) {
            wordSet.add(w);
            if(w.length > maxWord.length) {
                maxWord = w;
            } else if(w.length === maxWord.length && w < maxWord) {
                maxWord = w;
            }
        }
    }
    return maxWord;
};

微信截图_20220817120021.png

解题思路——Trie树

老观众都知道 Trie 树了,看到什么 “词典” “单词” 啊这种东西,都应该联系到 Trie 树,毕竟人家也叫单词搜索树是吧。

题解

class Trie {
    constructor() {
        this.children = Object.create(null);
    }
    insert(word) {
        let children = this.children;
        for(const w of word) {
            if(children[w] === void 0) {
                children[w] = new Trie();
            }
            children = children[w].children;
        }
        children['#'] = true;
    }
}
var longestWord = function(words) {
    const root = new Trie();

    for(const w of words) {
        root.insert(w);
    }

    let maxCnt = -Infinity, maxWord = '';
    for(const w of words) {
        const t = searchLongerstWord(w, root);
        if(t.length > maxCnt) {
            maxCnt = t.length;
            maxWord = t;
        } else if(t.length === maxCnt && t < maxWord) {
            maxCnt = t.length;
            maxWord = t;
        }
    }
    return maxWord;
};

const searchLongerstWord = (word, trie) => {
    let children = trie.children, s='';
    for(const w of word) {
        
        children = children[w].children;
         if (children['#']) {
            s += w;
        } else {
            return s;
        }
    }
    return s;
}

1.png