【力扣刷题记 30】——《串联所有单词的子串》

Offer 驾到,掘友接招!我正在参与 2022 春招打卡活动,点击查看活动详情

一、题目描述:

  1. 串联所有单词的子串 —— 难度困难

给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]

输出:[0,9]

解释:

从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。

输出的顺序不重要, [9,0] 也是有效答案。

示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]

输出:[]

示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]

输出:[6,9,12]

提示:

1 <= s.length <= 10^4

s 由小写英文字母组成

1 <= words.length <= 5000

1 <= words[i].length <= 30

words[i] 由小写英文字母组成

二、题目和思路分析:

这道题比较难, 暴力破解肯定是不行的,因为1 <= s.length <= 10^41 <= words.length <= 50001 <= words[i].length <= 30,这些限定条件相当于堵死了暴力破解的路。

那么还有什么好方法呢?

除了在words上考虑,还可以在s上面下功夫好好想一下,因为s必定是要进行遍历的。这么想起来,只要遍历s,考虑s[i]——s[i+words.length]是否完全等于words即可,等于则返回i。

值得考虑的是:s[i]——s[i+words.length]是字符串,words是数组,那么如何进行快速对比呢?

如果把words转为对象,比如:

words = ['word', 'word', 'strr', 'intt']

转化为map对象就是:

{
    word: 2,
    strr: 1,
    intt: 1,
}

然后对s[i]——s[i+words.length]也进行转map对象的操作,遍历两个对象是否相等即可。
复制代码

困难的问题这么想,好像也挺简单的,看起来像是暴力破解,但还是比暴力破解优秀很多,并且中间可以判断首子串是否属于words,不属于则跳过。

既然思路看起来没问题,那么代码搞起来!

三、代码:

代码实现如下:

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    let len = words.length // words元素个数
    let wlen = words[0].length // words单个元素长度
    let wslen = len*wlen // words全部字符长度
    if(s.length < wslen) return [] // 如果s长度小于words所有单词拼接的字符串长度,直接返回[]
    let arr = []
    let wordsObj = new Map()
    for (let word of words) {
        let count = wordsObj.has(word) ? wordsObj.get(word) : 0
        wordsObj.set(word, count + 1)
    }
    for(let i = 0; i < s.length; i++){
        if(i + wslen > s.length) break
        if(words.includes(s.substr(i, wlen))){
            if (objEqual(s.substr(i, wslen))) {
                arr.push(i)
            }
        }else{
            continue
        }
    }

    return arr

    // 对比当前字符串转map对象后和wordObj是否一致
    function objEqual(str){
        let bool = true
        let map = new Map()
        for (let i = 0; i < len; i++) {
            let word = str.substring(i * wlen, (i + 1) * wlen)
            let count = map.has(word) ? map.get(word) : 0
            map.set(word, count + 1)
        }
        for (let [key, value] of wordsObj) {
            if (!map.has(key) || map.get(key) !== value) {
                bool = false
            }
        }
        console.log(bool)
        return bool
    }
};

复制代码

四、总结:

这次的困难题好像没那么难,但是优化我倒是写了好久,才优化了100ms,总之大部分的题目使用遍历都是能够解决的,我们要进一步考虑的话,就是想想如何把遍历写的更加优雅。

加油吧!

image.png

猜你喜欢

转载自juejin.im/post/7080533459500236807