一起刷LeetCode——串联所有单词的子串(滑动窗口)

95 阅读1分钟

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

串联所有单词的子串

给定一个字符串 s 和一个字符串数组 wordswords 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含  words 中所有字符串以任意顺序排列连接起来的子串。

返回所有串联字串在 s 中的开始索引。你可以以 任意顺序 返回答案。

分析

直接法

  • 最直接的方法就是得到所有s的子串,然后判断是否是words连接的子串,为了减少每次都要计算,使用Map来记录之前的搜索结果

代码

var wordLength, totalCount;
let search = function(s, wordCounts, i, used, matched) {
    if( matched == totalCount ) return true;
    if( i >= s.length ) return false;
    var j, testStr = s.substr(i,wordLength);
    if( wordCounts.has(testStr) ) {
        if(!used.has(testStr) ) {
            used.set(testStr, 1);
        } else if( used.get(testStr) < wordCounts.get(testStr) ) {
            used.set(testStr, used.get(testStr)+1);
        } else {
            return false;
        }
        return search(s, wordCounts, i+wordLength, used, matched+1);
    }
    return false;
}

var findSubstring = function(s, words) {
    let wordCounts = new Map();
    totalCount=0;
    for( let word of words ) {
        if( wordCounts.has(word) ) wordCounts.set(word, wordCounts.get(word)+1);
        else wordCounts.set(word, 1);
        totalCount++;
    }
    wordLength = words[0].length;
    let result=[];
    for( var i=0; i<s.length; i++ ) {
        if( search(s, wordCounts, i, new Map(), 0) ) {
            result.push(i);
        }
    }
    return result;
};

滑动窗口

  • 无重复字符的最长子串类似,在判断s中的子串是不是words中串联起来的子串的时候,这个时候在s上也有一个滑动窗口,滑动窗口长度是words中单词的长度和个数
  • 首先使用map来管理words中的单词
  • 然后用两个指针来表示当前窗口的范围,从而得到字符串的子串
  • 最后对子串中的单词进行分析,判断这个子串是否是单词串联起来的,如果是,就可以记录遍历过程中当前的index,如果不是就跳过,继续向后滑动这个窗口,直至遍历完字符串

代码

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    let map = new Map()
    let wordLength = words[0].length
    let wordCount = words.length
    let slideWindow = wordLength * wordCount
    for (let word of words) {
        map.has(word) ? map.set(word, map.get(word) + 1) : map.set(word, 1)
    }
    let leftIndex = 0
    let rightIndex = slideWindow - 1
    let result = []
    const helper = (tempStr) => {
        let visited = new Map()
        for (let i = 0; i < tempStr.length; i+= wordLength) {
            let word = tempStr.substr(i, wordLength)
            visited.has(word) ? visited.set(word, visited.get(word) + 1) : visited.set(word, 1)
        }
        for (let [key, val] of visited) {
            if (map.get(key) != val) return false
        }
        return true
    }
    
    while (rightIndex < s.length) {
        if (rightIndex - leftIndex + 1 == slideWindow) {
            let tempStr = s.substring(leftIndex, rightIndex + 1)
            if (helper(tempStr)) result.push(leftIndex)
            leftIndex++
        }
        rightIndex++
    }
    
    return result
};

总结

  • 今天的题目推荐使用滑动窗口的解法,但是直接法按照题目的意思去找结果也是可以的,在一些不追求算法的时间复杂度和空间复杂度的情况下,直接法可能是最常用并且不会出错的方法
  • 今天也是有收获的一天