[30] 串联所有单词的子串 js

题目描述:

给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。

*

* s 中的 串联子串 是指一个包含  words 中所有字符串以任意顺序排列连接起来的子串。

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

* 输出:[0,9]

* 解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。

* 子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。

* 子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。

* 输出顺序无关紧要。返回 [9,0] 也是可以的。

解题思路:

  1. 一开始的想法是,以字符串数组中单词的长度为单位往前走,反正都是固定长度,然后维护一个map来记录单词出现的情况,但是忽略了单词可以重复出现,所以遇到相同的单词的时候会出现问题,因为我的map里已经有一个了,然后我直接跳过去了。
  2. 这个时候我发现一个map 没办法来判断单词出现的次数是不是符合本来要出现的次数,还需要一个基准map,所以用到了两个map来判断出现的次数是不是对的。
  3. 但是忽略了一种情况是单词是可以截断的,他出现的位置不一定就是按照单词的长度的整数倍来出现的,比如:
    
    findSubstring('lingmindraboofooowingdingbarrwingmonkeypoundcake', ["fooo","barr","wing","ding","wing"])

    这个如果我按照4为步长前进,是找不到答案的。

  4. 所以步长更改为1,出现某些情况的时候从头开始来判断。这样做出来性能很差,只能算是暴力解法了。

解法一(暴力哈希):

function findSubstring(s, words) {
    let step = words[0].length;
    let start = 0;
    let end = step;
    let res = [];
    // 存放单词应该出现的次数
    let map = new Map();
    // 计算行进过程中出现了对的单词,就让次数减1,减到0说明个数到了
    let wordMap = new Map();

    // 先记录所有单词出现的次数
    for (let i = 0; i < words.length; i++) {
        if (map.get(words[i])) {
            map.set(words[i], map.get(words[i]) + 1);
            wordMap.set(words[i], wordMap.get(words[i]) + 1);
            continue;
        }
        map.set(words[i], 1);
        wordMap.set(words[i], 1);
    }

    while (end <= s.length) {
        // 当前正在判断的单词
        let word = s.substring(end - step, end);
        // 如果目标里没有这个单词,就往前进一个,同时清空记录的表
        if (!map.has(word)) {
            map.forEach((value, key) => {
                wordMap.set(key, value);
            });
            start = start + 1;
            end = start + step;
            continue;
        }
        // 如果当前单词在目标单词里面,并且这个单词出现次数还没超,就往前进一个单词的长度
        if (wordMap.has(word) && wordMap.get(word) > 0) {
            wordMap.set(word, wordMap.get(word) - 1);
            end = end + step;
        } else {
            // 如果不需要这个单词,或者这个单词已经次数满了,就从头前进一位,从头再来
            map.forEach((value, key) => {
                wordMap.set(key, value);
            });
            start = start + 1;
            end = start + step;
        }
        // 每次判断窗口长度是否超过,如果相等,而前面没有跳出去,说明目前为止都符合,把下标塞进要返回的数组里,再从头开始
        if ((end - step - start) / step === words.length) {
            res.push(start);
            map.forEach((value, key) => {
                wordMap.set(key, value);
            });
            start = start + 1;
            end = start + step;
        }
    }
    return res;
};

用时:

// Your runtime beats 16.67 % of typescript submissions

// Your memory usage beats 98.89 % of typescript submissions (48.1 MB)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值