携手创作,共同成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情
串联所有单词的子串
给定一个字符串
s
和一个字符串数组words
。words
中所有字符串 长度相同。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
};
总结
- 今天的题目推荐使用滑动窗口的解法,但是直接法按照题目的意思去找结果也是可以的,在一些不追求算法的时间复杂度和空间复杂度的情况下,直接法可能是最常用并且不会出错的方法
- 今天也是有收获的一天