30. Substring with Concatenation of All Words
Description
You are given a string s
and an array of strings words
. All the strings of words
are of the same length.
A concatenated string is a string that exactly contains all the strings of any permutation of words
concatenated.
- For example, if
words = ["ab","cd","ef"]
, then"abcdef"
,"abefcd"
,"cdabef"
,"cdefab"
,"efabcd"
, and"efcdab"
are all concatenated strings."acdbef"
is not a concatenated string because it is not the concatenation of any permutation ofwords
.
Return an array of the starting indices of all the concatenated substrings in s
. You can return the answer in any order.
Example 1:
Input: s = "barfoothefoobarman", words = ["foo","bar"]
Output: [0,9]
Explanation:
The substring starting at 0 is "barfoo"
. It is the concatenation of ["bar","foo"]
which is a permutation of words
.
The substring starting at 9 is "foobar"
. It is the concatenation of ["foo","bar"]
which is a permutation of words
.
Example 2:
Input: s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
Output: []
Explanation:
There is no concatenated substring.
Example 3:
Input: s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
Output: [6,9,12]
Explanation:
The substring starting at 6 is "foobarthe"
. It is the concatenation of ["foo","bar","the"]
.
The substring starting at 9 is "barthefoo"
. It is the concatenation of ["bar","the","foo"]
.
The substring starting at 12 is "thefoobar"
. It is the concatenation of ["the","foo","bar"]
.
Constraints:
1 <= s.length <= 104
1 <= words.length <= 5000
1 <= words[i].length <= 30
s
andwords[i]
consist of lowercase English letters.
Solutions
Solution: Sliding Window + Hash Map
- Time complexity: O(n)
- Space complexity: O(n)
JavaScript
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
const findSubstring = function (s, words) {
const wordsMap = words.reduce((map, word) => {
const count = map.get(word) ?? 0;
return map.set(word, count + 1);
}, new Map());
const currentMap = new Map();
const n = s.length;
const length = words[0].length;
const result = [];
const getSubStrCount = str => currentMap.get(str) ?? 0;
for (let start = 0; start < length; start++) {
let left = start;
let currentCount = 0;
for (let index = start; index <= n - length; index += length) {
const subStr = s.slice(index, index + length);
const count = wordsMap.get(subStr);
if (count) {
currentMap.set(subStr, getSubStrCount(subStr) + 1);
if (getSubStrCount(subStr) <= count) currentCount += 1;
else {
while (getSubStrCount(subStr) > count) {
const leftSubStr = s.slice(left, left + length);
currentMap.set(leftSubStr, getSubStrCount(leftSubStr) - 1);
left += length;
if (getSubStrCount(leftSubStr) >= wordsMap.get(leftSubStr)) continue;
currentCount -= 1;
}
}
if (currentCount === words.length) {
const leftSubStr = s.slice(left, left + length);
result.push(left);
currentMap.set(leftSubStr, getSubStrCount(leftSubStr) - 1);
currentCount -= 1;
left += length;
}
continue;
}
currentCount = 0;
currentMap.clear();
left = index + length;
}
currentMap.clear();
}
return result;
};