30. 串联所有单词的子串
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"]
- 输出:
[]
题目模板
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
};
2.思路分析
- words排列组合
- 寻找各个子串在s中的初始位置并push进arr作为结果返回
本题重点是实现一个排列组合算法,实现原理如图:
细节详见题解
3.所用到的方法
见题解
4.题解及优化
我的题解
let findSubstring = (s, words) => {
let res = []
let rewords = []
let len = words.length
/**
* 【排列组合算法】
* 说明:func用来对words中的元素进行排列组合,将排列好的各个结果存在新数组中,并返回
* @param arr:排列好的元素
* @param left_words:待排列元素
*/
let func = (arr, left_words) => {
if (arr.length === len) {
rewords.push(arr)
} else {
left_words.forEach((item, index) => {
let temp = [].concat(left_words)
temp.splice(index, 1)
// 此时,第一个参数是当前分离出的元素所在数组;第二个参数temp是传入的new_words去掉第一个后的结果
func(arr.concat(item), temp)
})
}
}
func([], words)
rewords.map(
item => {
for (let i = 0; i < s.length; i++) {
i = s.indexOf(item.join(''), i) // 找到每个子串在s中的位置
if (i === -1 || item.length === 0) {
break
} else {
res.push(i)
}
}
}
)
return [...new Set(res.filter(value => value >= 0).sort())] // 筛选剔除-1项
}
console.log('结果', findSubstring('a', [])) // []
// console.log('结果', findSubstring('foobarfoobar', ['foo', 'bar'])) // 0,3,6
// console.log('结果', findSubstring('wordgoodgoodgoodbestword', ['word', 'good', 'best', 'good'])) // 8
// console.log('结果', findSubstring('CADDBCACDBDACCAADBCA', ['A', 'B', 'C', 'D'])) // 3,6,9,15,16
注意,本题有两个容易忽视的点:
- 去重:words中含有相同word时,将会出现两个相同的子串,也就会导致结果中有重复。
- 测试用例:
findSubstring('wordgoodgoodgoodbestword', ['word', 'good', 'best', 'good']) // 8
- 解决方法:去重,子串数组去重和结果去重都可以,如果中间结果中待加工“子串”是二维数组的话去重会麻烦些,可以直接在结果中去重,将
return res
改为return [...new Set(res)]
,子串数组去重可以采用在push前使用includes判断。- 多个符合匹配的位置
- 测试用例:findSubstring(‘foobarfoobar’, [‘foo’, ‘bar’]) // 0,3,6
- 解决方法:将:
res = rewords.map(i => s.indexOf(i.join(''))).filter(v => v >= 0).sort()
修改为如下:
rewords.map(
item => {
for (let i = 0; i < s.length; i++) {
i = s.indexOf(item.join(''), i) // 找到每个子串在s中的位置
if (i === -1) {
break
} else {
res.push(i)
}
}
}
)
res = res.filter(value => value >= 0).sort()
当数据量大的时候组合数太多导致内存溢出。。。最终我的题解被下面这个测试用例弄崩溃了。。。:
"pjzkrkevzztxductzzxmxsvwjkxpvukmfjywwetvfnujhweiybwvvsrfequzkhossmootkmyxgjgfordrpapjuunmqnxxdrqrfgkrsjqbszgiqlcfnrpjlcwdrvbumtotzylshdvccdmsqoadfrpsvnwpizlwszrtyclhgilklydbmfhuywotjmktnwrfvizvnmfvvqfiokkdprznnnjycttprkxpuykhmpchiksyucbmtabiqkisgbhxngmhezrrqvayfsxauampdpxtafniiwfvdufhtwajrbkxtjzqjnfocdhekumttuqwovfjrgulhekcpjszyynadxhnttgmnxkduqmmyhzfnjhducesctufqbumxbamalqudeibljgbspeotkgvddcwgxidaiqcvgwykhbysjzlzfbupkqunuqtraxrlptivshhbihtsigtpipguhbhctcvubnhqipncyxfjebdnjyetnlnvmuxhzsdahkrscewabejifmxombiamxvauuitoltyymsarqcuuoezcbqpdaprxmsrickwpgwpsoplhugbikbkotzrtqkscekkgwjycfnvwfgdzogjzjvpcvixnsqsxacfwndzvrwrycwxrcismdhqapoojegggkocyrdtkzmiekhxoppctytvphjynrhtcvxcobxbcjjivtfjiwmduhzjokkbctweqtigwfhzorjlkpuuliaipbtfldinyetoybvugevwvhhhweejogrghllsouipabfafcxnhukcbtmxzshoyyufjhzadhrelweszbfgwpkzlwxkogyogutscvuhcllphshivnoteztpxsaoaacgxyaztuixhunrowzljqfqrahosheukhahhbiaxqzfmmwcjxountkevsvpbzjnilwpoermxrtlfroqoclexxisrdhvfsindffslyekrzwzqkpeocilatftymodgztjgybtyheqgcpwogdcjlnlesefgvimwbxcbzvaibspdjnrpqtyeilkcspknyylbwndvkffmzuriilxagyerjptbgeqgebiaqnvdubrtxibhvakcyotkfonmseszhczapxdlauexehhaireihxsplgdgmxfvaevrbadbwjbdrkfbbjjkgcztkcbwagtcnrtqryuqixtzhaakjlurnumzyovawrcjiwabuwretmdamfkxrgqgcdgbrdbnugzecbgyxxdqmisaqcyjkqrntxqmdrczxbebemcblftxplafnyoxqimkhcykwamvdsxjezkpgdpvopddptdfbprjustquhlazkjfluxrzopqdstulybnqvyknrchbphcarknnhhovweaqawdyxsqsqahkepluypwrzjegqtdoxfgzdkydeoxvrfhxusrujnmjzqrrlxglcmkiykldbiasnhrjbjekystzilrwkzhontwmehrfsrzfaqrbbxncphbzuuxeteshyrveamjsfiaharkcqxefghgceeixkdgkuboupxnwhnfigpkwnqdvzlydpidcljmflbccarbiegsmweklwngvygbqpescpeichmfidgsjmkvkofvkuehsmkkbocgejoiqcnafvuokelwuqsgkyoekaroptuvekfvmtxtqshcwsztkrzwrpabqrrhnlerxjojemcxel"
["dhvf","sind","ffsl","yekr","zwzq","kpeo","cila","tfty","modg","ztjg","ybty","heqg","cpwo","gdcj","lnle","sefg","vimw","bxcb"]
课程解法
let findSubstring = (s, words) => {
// 计算字符串的总长度
let strLen = s.length
// 计算所有的单词数量
let wordsLen = words.length
// 计算所有单词出现的起始位置和截止位置
let pos = {}
// 如果字符串的长度小于所有单词的总长度直接返回
if (strLen < words.join('').length) {
return []
}
// 遍历所有单词查找在字符串中的起始位置和截止位置
words.every(word => {
if (pos[word]) {
return true
}
let wl = word.length
let tmp = []
for (let i = 0, len = strLen - wl, idx; i <= len; i++) {
idx = s.slice(i).indexOf(word)
if (idx > -1) {
if (idx === 0) {
tmp.push({
start: i,
end: i + wl
})
} else if (s[i + 1] !== word[0]) {
i += idx - 1
}
} else {
break
}
}
// 如果没有匹配到单词终止遍历
if (tmp[0] === undefined) {
return false
} else {
// 保存当前单词的位置,遍历下一个单词
pos[word] = tmp.sort((a, b) => a.start - b.start)
return true
}
})
// 只要有一个单词没找到说明不能匹配到连续的字符串
if (words.find(item => !pos[item])) {
return []
}
let result = []
// 计算所有单词的位置
let match = (poses) => {
// 记录是不是所有单词都被匹配到了,每一次都应该把所有单词都包括进来并且是相邻的
let record = []
let len = Object.keys(poses).length
// 如果没有单词的位置说明处理结束了
if (len < 1) {
return -1
}
while (1) {
// 每次循环应该把记录清空
record.length = 0
// 按照起始位置进行升序排序
let minV = Number.MAX_SAFE_INTEGER
let minK = ''
// 优先找到所有单词其实位置最小的单词开始匹配
for (let [k, v] of Object.entries(poses)) {
if (!v.length) {
return false
} else {
if (v[0].start < minV) {
minK = k
minV = v[0].start
}
}
}
if (!minK) {
return false
}
// 起始位置最小的单词
let first = poses[minK].shift()
if (!first) {
return false
}
// 记录下这个起始位置
let start = first.start
// 记录words列表中的单词
record.push(words.findIndex(item => item === minK))
// 每次循环要匹配到所有单词
for (let i = 1; i < wordsLen; i++) {
for (let j = 0, next; j < wordsLen; j++) {
if (record.includes(j)) {
continue
}
if (poses[words[j]][0] === undefined) {
return false
}
next = poses[words[j]].find(item => item.start === first.end)
if (next) {
record.push(j)
first = next
break
}
}
}
// 如果所有单词的顺序是挨着的,记录下当前的起始位置
if (record.length === wordsLen && !record.find(item => item === undefined)) {
result.push(start)
}
}
}
match(pos)
// 对 result 去重,如 result=[1,1,2,3] [...new Set(result)]===[1,2,3]
return [...new Set(result)]
}
算法是利用查找每个单词在字符串的位置,然后通过计算这些位置是不是连续的。比如 abfoobarcd,[foo,bar],那么for的起始位置是2,bar的起始位置是5;说明这两个单词是连续的2+3(for的长度)=5
foo:[{start:2,end:5}]
bar:[{start:5,end:8}]
判断上一个单词的end和下一个单词的start是不是相同来计算两个单词是不是挨着
其他小伙伴的解法
解法一:暴力求解
let findSubstring = (s, words) => {
if (!words || !words.length) return[];
let wordLen = words[0].length;
let allWordsLen = wordLen * words.length;
let ans = [], wordMap = {};
for (let w of words) {
wordMap[w] ? wordMap[w]++ :wordMap[w] = 1
}
for (let i = 0; i < s.length - allWordsLen + 1; i++) {
let wm = Object.assign({}, wordMap);
for (let j = i; j < i + allWordsLen - wordLen + 1; j += wordLen) {
let w = s.slice(j, j + wordLen);
if (wm[w]) {
wm[w]--
} else {
break;
}
}
if (Object.values(wm).every(n => n === 0)) ans.push(i);
}
return ans;
}
思路详见原作者博客
解法二:滑动窗口
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
if (!s || !words || !words.length) return [];
let windows = {}, needs = {}, oneWordLen = words[0].length;
for (let w of words) {
needs[w] ? needs[w]++ : needs[w] = 1;
}
let l = 0, r = 0, count = 0, needsKeyLen = Object.keys(needs).length, ans = [];
for (let i = 0; i < oneWordLen; i++) {
windows = {};
r = l = i;
count = 0;
while (r <= s.length - oneWordLen) {
let w1 = s.slice(r, r + oneWordLen);
r += oneWordLen;
if (!needs[w1]) {
windows = {};
l = r;
count = 0;
continue;
}
windows[w1] ? windows[w1]++ : windows[w1] = 1;
if (windows[w1] === needs[w1]) count++;
while (count === needsKeyLen) {
if (r - l === oneWordLen * words.length) ans.push(l);
let w2 = s.slice(l, l + oneWordLen);
l += oneWordLen;
if (needs[w2]) {
windows[w2]--;
if (windows[w2] < needs[w2]) count--;
}
}
}
}
return ans;
};
思路详见原作者博客
更加过分的测试用例:
"abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab"
["ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba","ab","ba"]