Open azl397985856 opened 2 years ago
30. 串联所有单词的子串
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words
前置知识
- 哈希表
- 双指针
题目描述
给定一个字符串 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"] 输出:[]
*len(words[0)的字符串,然后计算是否符合要求。
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
#对于s中每一个和len(words)长度相同的字符串,都比较确认它能否由
len1=len(words[0])*len(words)
neededDict={}
for word in words:
if word not in neededDict:
neededDict[word]=1
else:
neededDict[word]+=1
allRes=[]
for i in range(len(s)-len1+1):
s1=s[i:i+len1]
#s1是否可以由words拼凑而成
#s1等量划分
parts=[]
n1=len(s1)//len(words[0])
for j in range(n1):
parts.append(s1[j*len(words[0]):(j+1)*len(words[0])])
#开始比对字符出现次数
dict1={}
valid=0
for part1 in parts:
if part1 not in dict1:
dict1[part1]=1
else:
dict1[part1]+=1
if part1 in neededDict and dict1[part1]==neededDict[part1]:
valid+=1
if valid==len(neededDict):
allRes.append(i)
return allRes
时间复杂度:O(N*M),N为s的长度,M为words中word单词个数 空间复杂度:O(M)
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
if (s == null || words == null || words.length == 0) {
return new ArrayList<>();
}
List<Integer> res = new ArrayList<>();
int n = words.length;
int m = words[0].length();
HashMap<String, Integer> map = new HashMap<>();
for (String str : words) {
map.put(str, map.getOrDefault(str, 0) + 1);
}
for (int i = 0; i <= s.length() - n * m; i++) {
HashMap<String, Integer> copy = new HashMap<>(map);
int k = n;
int j = i;
while (k > 0) {
String str = s.substring(j, j + m);
if (!copy.containsKey(str) || copy.get(str) < 1) {
break;
}
copy.put(str, copy.get(str) - 1);
k--;
j += m;
}
if (k == 0) res.add(i);
}
return res;
}
}
先用全排列,列出words中所有组合。 然后根据长度截断s中的字符,判断是否在string组合里面。 缺点: 该算法时间复杂度较高,后期完善。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
if (s.empty()) return res;
set<string> strs;
string tmp;
helper(tmp, 0, words.size() - 1, words, strs);
for (auto iter = strs.begin(); iter != strs.end(); iter++) {
int length = iter->length();
for (int j = 0; j + length <= s.length(); j++) {
if (s.substr(j, length) == *iter) {
res.push_back(j);
}
}
}
return res;
}
void helper(string& s, int start, int end,
vector<string>& words, set<string>& strs) {
if (start >= end) {
for (auto tmp_s : words) { s += tmp_s; }
strs.insert(s);
s.clear();
return;
}
for (int i = start; i <= end; i++) {
swap_str(words[start], words[i]);
helper(s, start+1, end, words, strs);
swap_str(words[start], words[i]);
}
return;
}
void swap_str(string& a, string& b) {
string tmp = a;
a = b;
b = tmp;
return;
}
};
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < words.length; i++) {
map.put(words[i], map.getOrDefault(words[i], 0) + 1);
}
int wordLen = words[0].length();
int totalLen = wordLen * words.length;
for (int i = 0; i <= s.length() - totalLen; i++) {
Map<String, Integer> temp = new HashMap<>();
for (int j = 0; j < words.length; j++) {
String substr = s.substring(i + j * wordLen, i + (j+1) * wordLen);
if (!map.containsKey(substr)) break;
temp.put(substr, temp.getOrDefault(substr, 0) + 1);
if (temp.get(substr) > map.get(substr)) break;
if (j == words.length-1) list.add(i);
}
}
return list;
}
}
Time O(NMK). N: length of s, M: size of words array, K: length of word in words Space O(M * K)
class Solution
{
public:
vector<int> findSubstring(string s, vector<string> &words)
{
std::unordered_map<std::string, int> window;
std::unordered_map<std::string, int> needed;
for (auto &w : words) {
if (needed.count(w) == 0) {
needed.emplace(w, 1);
} else {
needed[w]++;
}
}
std::vector<int> res;
int wsize = words[0].size();
int wordssize = wsize * words.size();
for (int ii = 0; ii < wsize; ii++) {
window.clear();
int valid{ 0 };
int left{ ii };
int right{ ii };
// cout << "wsize: " << wsize << " wordsize: " << wordssize << endl;
while (right < s.size() - wsize + 1) {
// Move faster pointer one step forward
std::string rightStr = s.substr(right, wsize);
right += wsize;
// cout << "window: [" << left << ", " << right << ")" << endl;
// Update the window if necessary
if (needed.count(rightStr) > 0) {
if (window.count(rightStr) == 0) {
window.emplace(rightStr, 1);
} else {
window[rightStr]++;
}
if (window[rightStr] == needed[rightStr]) {
valid++;
}
// cout << " String to add: "<< rightStr << ", valid now: " << valid << endl;
}
// Try to shrink the window
while (right - left >= wordssize) {
if (valid == needed.size()) {
res.push_back(left);
}
std::string leftStr = s.substr(left, wsize);
left += wsize;
if (needed.count(leftStr) > 0) {
if (window[leftStr] == needed[leftStr]) {
valid--;
}
window[leftStr]--;
}
}
}
}
return res;
}
};
今天实在没有想出来方法,直接学习官方解答
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
if (words == null || words.length == 0)
return res;
for (String word : words)
map.put(word, map.getOrDefault(word, 0) + 1);
int sLen = s.length(), wordLen = words[0].length(), count = words.length;
int match = 0;
for (int i = 0; i < sLen - wordLen * count + 1; i++) {
//得到当前窗口字符串
String cur = s.substring(i, i + wordLen * count);
Map<String, Integer> temp = new HashMap<>();
int j = 0;
for (; j < cur.length(); j += wordLen) {
String word = cur.substring(j, j + wordLen);
// 剪枝
if (!map.containsKey(word))
break;
temp.put(word, temp.getOrDefault(word, 0) + 1);
// 剪枝
if (temp.get(word) > map.get(word))
break;
}
if (j == cur.length())
res.add(i);
}
return res;
}
}
T: O(n∗m∗k) S: O(m)
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
n = len(s)
m = len(words)
w = len(words[0])
dict_map = dict()
for word in words:
dict_map[word] = dict_map.get(word,0) + 1
res = []
if n < m*w:
return []
i = 0
for j in range(w*m,n+1):
dict_w = dict()
tmp = s[i:j]
for k in range(0,len(tmp),w):
dict_w[tmp[k:k+w]] = dict_w.get(tmp[k:k+w],0 ) + 1
if dict_w == dict_map:
res.append(i)
i+=1
return res
https://leetcode.com/problems/substring-with-concatenation-of-all-words/
word length * word count
(as if there are two pointers, but use substring method actually).word length
.
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> result = new ArrayList<>();
Map<String, Integer> wordsMap = new HashMap<>();
for(String word: words){
wordsMap.put(word, wordsMap.getOrDefault(word, 0) + 1);
}
int wordLen = words[0].length();
int wordsNum = words.length;
for(int i = 0; i < s.length() - wordLen * wordsNum + 1; i++){
String curr = s.substring(i, i + wordLen * wordsNum);
Map<String, Integer> currMap = new HashMap<>();
int j = 0;
for(; j < curr.length(); j += wordLen){
String slice = curr.substring(j, j + wordLen);
if(!wordsMap.containsKey(slice)){
break;
}
currMap.put(slice, currMap.getOrDefault(slice, 0) + 1);
if(currMap.get(slice) > wordsMap.get(slice)){
break;
}
}
if(j == curr.length()){
result.add(i);
}
}
return result;
}
}
First, use a map freqMap
to count the frequencies of all words.
Then use sliding window to find all possible substrings, notice all strings in words
have the same length, so we can increment the left/right boundary by l
, where l
is the length of each word.
CPP
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int m = s.size(), n = words.size(), l = words[0].size();
unordered_map<string, int> freqMap;
for (auto it = words.begin(); it != words.end(); ++it) {
freqMap[*it]++;
}
// Perform two pointers
vector<int> res;
for (int start = 0; start < l; ++start) {
int left = start, right = start, ctr = 0;
unordered_map<string, int> map;
while (right < m) {
string str = s.substr(right, l);
// If this this word not exist, start at next one
if (freqMap.find(str) == freqMap.end()) {
map.clear();
ctr = 0;
right += l;
left = right;
} else if (map[str] == freqMap[str]) {
// Need to move left bound.
map[s.substr(left, l)]--;
left += l;
--ctr;
} else if (map[str] < freqMap[str]) {
// Valid, forward right ptr.
map[str]++;
right += l;
++ctr;
}
if (ctr == n) {
res.push_back(left);
--ctr;
map[s.substr(left, l)]--;
left += l;
}
}
}
return res;
}
};
Time: O(m*n*l)
, where m
is the length of s
, n
is the length of words
, and l
is the length of each word, which is words[0].size()
.
Space: O(n)
, for map.
打卡, 回头看
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
if (words == null || words.length == 0)
return res;
for (String word : words)
map.put(word, map.getOrDefault(word, 0) + 1);
int sLen = s.length(), wordLen = words[0].length(), count = words.length;
int match = 0;
for (int i = 0; i < sLen - wordLen * count + 1; i++) {
//得到当前窗口字符串
String cur = s.substring(i, i + wordLen * count);
Map<String, Integer> temp = new HashMap<>();
int j = 0;
for (; j < cur.length(); j += wordLen) {
String word = cur.substring(j, j + wordLen);
// 剪枝
if (!map.containsKey(word))
break;
temp.put(word, temp.getOrDefault(word, 0) + 1);
// 剪枝
if (temp.get(word) > map.get(word))
break;
}
if (j == cur.length())
res.add(i);
}
return res;
}
}
class Solution(object):
def findSubstring(self, s, words):
hash = {}
res = []
wsize = len(words[0])
for str in words:
if str in hash:
hash[str] += 1
else:
hash[str] = 1
for start in range(0, len(words[0])):
slidingWindow = {}
wCount = 0
for i in range(start, len(s), wsize):
word = s[i : i + wsize]
if word in hash:
if word in slidingWindow:
slidingWindow[word] += 1
else:
slidingWindow[word] = 1
wCount += 1
while hash[word] < slidingWindow[word]:
pos = i - wsize * (wCount - 1)
removeWord = s[pos : pos + wsize]
print i, removeWord
slidingWindow[removeWord] -= 1
wCount -= 1
else:
slidingWindow.clear()
wCount = 0
if wCount == len(words):
res.append(i - wsize * (wCount - 1))
return res
O(mn)
思路: 今天又是不会然后学学的一天。 滑动窗口 + 哈希表的题型整体思路: 先把要查的子表遍历一下,用一个哈希表装每个单词的次数,用Allcount装整体单词出现的次数。
审题: 本题规定了,子串的单词数量相等,因此我们可以每次移动单词的步数 因此,大循环为 0 < n < len(word) 小滑动窗口是 left , right := i, i+len(word),然后for循环看right是否≤len(s),用第二个哈希表装字串值 窗口里面,每次都把最后的s[right, right - lne(word)]的单词拿出来,若该单词是字串的单词,则临时哈希表对应的单词次数++ 如果此时单词的数量与字串单词的数量相等,则Count ++ 然后当长度大于字串长度时,左指针进行移动,同样取左子串出来,如果临时里面有则--,如果临时的单词次数不等于字串单词次数,则Count-- 最后,只有Count ==AllCount 且 字串长度 right - left == oneWordL *wL 则添加到最终数组里
func findSubstring(s string, words []string) []int {
wL := len(words)
oneWordL := len(words[0])
hm := make(map[string]int)
Allcount := 0
for i:=0; i < wL; i++{
if _, ok := hm[words[i]];!ok{
Allcount++
}
hm[words[i]]++
}
ans := []int{}
for i:=0; i < oneWordL;i++{
Count := 0
tmpHm := map[string]int{}
left, right := i, i+oneWordL
for ; right <= len(s); right += oneWordL{
str := s[right-oneWordL:right]
if _, ok := hm[str];ok{
tmpHm[str]++
if tmpHm[str] == hm[str]{
Count++
}
}
for right - left > oneWordL * wL{
str := s[left:left+oneWordL]
if _, ok := tmpHm[str];ok{
tmpHm[str]--
}
if tmpHm[str]+1 == hm[str]{
Count--
}
left += oneWordL
}
if Count == Allcount && right - left == oneWordL * wL{
ans = append(ans, left)
}
}
}
return ans
}
时间复杂度:O(nmk) 空间复杂度:O(n)
这道题就是滑动窗口题目的一个变体。窗口内的字符组成的substring由给出的word组成。 所以,窗口右边界每次前进一个word的长度,但是左边每次前进一个char的长度。
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> ret = new ArrayList<>();
if (s == null || s.length() == 0) return ret;
Map<String, Integer> map = new HashMap<>();
for (String w : words) {
map.put(w, map.getOrDefault(w, 0) + 1);
}
int len = words[0].length();
int n = words.length;
for (int i = 0; i <= s.length() - n * len; i++) {
int start = i;
int end = i;
HashMap<String, Integer> copy = new HashMap<>(map);
while (end <= s.length() - len) {
String word = s.substring(end, end + len);
end += len;
if (!copy.containsKey(word)) {
break;
}
copy.put(word, copy.get(word) - 1);
if (copy.get(word) == 0) {
copy.remove(word);
}
if (copy.isEmpty()) {
ret.add(start);
}
}
}
return ret;
}
}
时间:O(n*m)
空间:O(m)
/**
function match(str, wordMap, wordNum, wordLen) { let map = new Map(); for (let i = 0; i < wordNum; i++) { let word = str.substring(i wordLen, (i + 1) wordLen); let count = map.has(word) ? map.get(word) : 0; map.set(word, count + 1); } let matchflag = true; for (let [key, value] of wordMap) { if (!map.has(key) || map.get(key) !== value) { matchflag = false; } } return matchflag; }
https://leetcode.com/problems/substring-with-concatenation-of-all-words/
Easy
Medium
Use one hashtable to store the word count. Scan the s by sliding window.
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
# find word counter dict
d = collections.Counter(words)
word_len, word_num, result = len(words[0]), len(words), []
# sliding windows
for i in range(0, len(s) - word_len * word_num + 1):
temp_d = copy.copy(d)
for j in range(word_num):
w = s[i+j*word_len : i+(j+1)*word_len]
if w in temp_d:
temp_d[w] -=1
if temp_d[w] == 0:
del temp_d[w]
else:
break
if len(temp_d) == 0:
result.append(i)
return result
时间复杂度: O(NKM) N=len(s), K=word_len M=word_sum 空间复杂度:O(K*M)
data range words can contain duplicates
build map of <word, freq>
for each start of subtring of length = unitLen*wordNum
- for each word, populate the map
- do pruning: not in given dictionary, > required freq
if find all the words, add start to result indices
Time:
O(words.length()) + O(s.length() - unitLen*wordNum) * [O(unitLen*wordNum) + O(wordNum) * O(unitLen)]
-> O(s.length() - unitLen*wordNum) * O(wordNum) * O(unitLen)
Space:two maps
O(number of unique words)
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> indices = new ArrayList<>();
// get freq
Map<String, Integer> wordFreq = new HashMap<>();
for (String word : words) {
wordFreq.put(word, wordFreq.getOrDefault(word, 0) + 1);
}
int unitLen = words[0].length(), wordNum = words.length;
// sliding window
// len to check = word.length * num of words
// index to check: [0, end], s.length()-1 - end + 1=word.length * num of words
// end = s.length() - unitLen*wordNum
for (int start = 0; start <= s.length() - unitLen*wordNum; start++) {
String curSub = s.substring(start, start + unitLen*wordNum);
// use a curFreqMap to see the difference
Map<String, Integer> curWordFreq = new HashMap<>();
int wordIndex = 0;
// within curSub
for (wordIndex = 0; wordIndex + unitLen <= curSub.length(); wordIndex += unitLen) { // note the incremental
String curUnit = curSub.substring(wordIndex, wordIndex + unitLen);
// not in given dictionary
if (!wordFreq.containsKey(curUnit)) {
break;
}
curWordFreq.put(curUnit, curWordFreq.getOrDefault(curUnit, 0) + 1);
// > required freq
if (curWordFreq.get(curUnit) > wordFreq.get(curUnit)) {
break;
}
}
// if finish the check, bingo
if (wordIndex == curSub.length()) {
indices.add(start);
}
}
return indices;
}
}
思路
暴力枚举匹配,有点滑动窗口的思想。使用哈希表记录单词的频率,然后对字符串进行遍历。每个长度单词相同,让题目更加简单一些 每次对字符串进行一个单词的匹配,如果匹配上就更新频率 使用k标记消除的次数。如果k为0则表示哈希表中全部匹配上
代码
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
if ("".equals(s) || words == null){
return res;
}
HashMap<String, Integer> map = new HashMap<>();
Integer sum = 0;
Integer wordLength = words[0].length();
for (String word : words) {
map.put(word,map.getOrDefault(word,0) + 1);
sum += word.length();
}
for (int i = 0; i < s.length() - sum + 1; i++) {
Map<String, Integer> copyMap = new HashMap<>(map);
int k = words.length;
int j = i;
while (k > 0){
String str = s.substring(j, j + wordLength);
if (!copyMap.containsKey(str) || copyMap.get(str) < 1){
break;
}
copyMap.put(str,copyMap.get(str) - 1);
k--;
j += wordLength;
}
if (k == 0){
res.add(i);
}
}
return res;
}
}
复杂度
时间复杂度:O(N^2)
空间复杂度:O(N)
官方解题思路
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
if (words == null || words.length == 0)
return res;
for (String word : words)
map.put(word, map.getOrDefault(word, 0) + 1);
int sLen = s.length(), wordLen = words[0].length(), count = words.length;
int match = 0;
for (int i = 0; i < sLen - wordLen * count + 1; i++) {
//得到当前窗口字符串
String cur = s.substring(i, i + wordLen * count);
Map<String, Integer> temp = new HashMap<>();
int j = 0;
for (; j < cur.length(); j += wordLen) {
String word = cur.substring(j, j + wordLen);
// 剪枝
if (!map.containsKey(word))
break;
temp.put(word, temp.getOrDefault(word, 0) + 1);
// 剪枝
if (temp.get(word) > map.get(word))
break;
}
if (j == cur.length())
res.add(i);
}
return res;
}
}
复杂度
用两个map,第一个map存words每个出现的次数,另外一个map存当前字符串下每个出现次数,如果两个map对应的次数相同,也认为该字符串起始位置满足条件
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
let chartLength = words[0].length
let wordLength = words.length
let map = {}
let res = []
words.forEach(key => {
map[key] = map[key]? map[key] + 1 : 1
})
for(let i =0; i<s.length - wordLength * chartLength + 1; i++) {
let y = s.slice(i, i + wordLength * chartLength)
let temp = {}
for (let j =0; j < y.length; ) {
let cur = y.slice(j, chartLength + j)
if (!map[cur]) {
break
} else {
temp[cur] = temp[cur] ? temp[cur] + 1 : 1
}
if ( temp[cur] > map[cur] ) {
break
}
j = j + chartLength
if (j == y.length) {
res.push(i)
}
}
}
return res
};
时间复杂度 O(nmk) 空间复杂度 O(m)
First, we can use hashmap to keep track count of each word. We iterate through the sentence. At each index, we check whether the window start with that index can be concatenated with the same words exactly once using the hashmap. If it is true, we add the index into the result.
var findSubstring = function(s, words) {
let hash_map={}
words.forEach(function(word,i){
if(hash_map[word] === undefined){
hash_map[word]=1
}else{
hash_map[word]+=1
}
})
let left=0
let total_length=(words.length*words[0].length)
const result = []
while (left<=s.length-total_length){
let hash_map_copy = Object.assign({}, hash_map);
let word_length=words[0].length
let count = words.length
let start= left
while(start<start+total_length){
if(hash_map_copy[s.slice(start,start+word_length)]===undefined ||
hash_map_copy[s.slice(start,start+word_length)] === 0) {
break
}else{
hash_map_copy[s.slice(start,start+word_length)]-=1
count -= 1
start+=word_length
}
}
if (count === 0) {
result.push(left)
}
left += 1
}
return result
};
Time:O(m(n-m)k) where n is the length of string, and m is the length of words
Space:O(m)
vclass Solution {
public List
if(wordCntMap.equals(cntMap)) { res.add(j - windowLength + stepLength); } } } return res; } } 、
class Solution: def findSubstring(self, s: str, words: List[str]) -> List[int]: from collections import Counter if not s or not words:return [] one_word = len(words[0]) all_len = len(words) * one_word n = len(s) words = Counter(words) res = [] for i in range(0, n - all_len + 1): tmp = s[i:i+all_len] c_tmp = [] for j in range(0, all_len, one_word): c_tmp.append(tmp[j:j+one_word]) if Counter(c_tmp) == words: res.append(i) return res
title: "Day 23 30. 串联所有单词的子串" date: 2021-10-02T13:49:39+08:00 tags: ["Leetcode", "c++", "unordered_map"] categories: ["algorithm"] draft: true
给定一个字符串 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"]
输出:[]
示例 3:
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
提示:
1 <= s.length <= 104
s 由小写英文字母组成
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 由小写英文字母组成
- 1、困难题目,开局思考半小时,发现应该可以使用滑动窗口的解法
- 2、具体思想为使用两个 unordered_map,一个 up 来记录总的 words 里单词和对应的数量,另一个 ump 用来记录遍历的滑动窗口内的 words 中的单词和对应的数量。
- 3、遍历是增加一个单词长度,按照 ws 去移动窗口,对于每次档次遍历,维持窗口 l = r
- 4、若单词在 ump 里不存在,重置窗口并清空 ump;
- 5、单词在 ump 里存在,有两种清空,单词出现的次数超出 ump 中的那一个单词的次数,所以需要将左边界增大,即右移 l,如果 cnt 都满足即等于 ns,则插入 l 作为当前的答案值
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> ans;
int n = s.size();
if(n <= 0 || words.empty()) return ans;
int ws = words[0].size(); //words中的word的长度,为统一长度
int ns = words.size(); //words中的word数量
unordered_map<string, int> up; //保存words中的word
for(auto & w : words)
{
up[w]++; //记录每一个单词出现的次数
}
for(int i = 0; i < ws; i++)
{
int l = i, r = i; //从左开始
int cnt = 0; //下文ump存放的单词总数!
unordered_map<string, int> ump;
while(r + ws <= n) //右边长度加每个单词长度要小于s的全长
{
string res = s.substr(r, ws); //在右边界加入一个单词
r += ws; //扩展右边界
if(up.find(res) != up.end()) //查看res在up中的位置
{
ump[res]++;
cnt++;
while(ump[res] > up[res])// 需要检查数量是否超过,超过则要右移left
{ //来缩小窗口
string tmp = s.substr(l, ws);
l += ws;
cnt--;
ump[tmp]--;
}
if(cnt == ns) ans.push_back(l);
}
else //未出现,舍弃此单词。清空
{
l = r;
cnt = 0;
ump.clear();
}
}
}
return ans;
}
};
时间复杂度:整体复杂度为 O(ns * ws)
空间复杂度:O(ns * ws),可能有一些问题,有空再来思考
思路 官方思路:以word里面总字符串长度,对s里面的子字符串进行遍历 每取s中子字符串的长度,就用map来检查是否能恰好用word里面的构成,不能右打断,不能右数量的不一致,如果有就break 代码
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
map<string, int> mapp;
int count = words.size();
if(words.empty() || count == 0)
{
return res;
}
for(auto s : words)
{
mapp[s] += 1;
}
int slen = s.size(), wordlen = words[0].size();
int match = 0;
int sumlen = wordlen*count;
for(int i = 0;i < slen - sumlen + 1;i++)
{
string cur = s.substr(i, sumlen);
map<string, int> temp;
int j = 0;
for(;j < cur.size();j+=wordlen)
{
string word = cur.substr(j, wordlen);
if(!mapp.count(word))
{
break;
}
temp[word] += 1;
if(temp[word] > mapp[word])
{
break;
}
}
if(j == cur.size())
{
res.push_back(i);
}
}
return res;
}
};
复杂度 时间复杂度:排序算法O((N-mk)m) 空间复杂度:存储所有的节点O(m),m为words个数
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList();
if (s.length() < 1) {
return null;
}
int len = words[0].length();
HashMap<String, Integer> map = new HashMap();
for (String str : words) {
map.put(str, map.getOrDefault(str, 0) + 1);
}
for (int i = 0; i < s.length(); i++) {
int left = i, right = left + len - 1;
HashMap<String, Integer> temp = new HashMap(map);
int solu = left;
while (right < s.length()) {
String sub = s.substring(left, right + 1);
//System.out.println(sub);
if (!temp.containsKey(sub)) {
break;
}
int count = temp.get(sub);
if (count > 1) {
temp.put(sub, count - 1);
} else {
temp.remove(sub);
}
if (temp.isEmpty()) {
res.add(solu);
break;
}
left = right + 1;
right = left + len - 1;
}
}
return res;
}
}
语言:C++
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
if (words.size() == 0) return res;
int n = s.size(), m = words.size(), w = words[0].size();
unordered_map<string, int> total;
for (string& word: words) total[word] ++;
for (int i = 0; i < w; i++) {
unordered_map<string, int> wd;
int cnt = 0;
for (int j = i; j + w <= n; j += w) {
if (j >= i + m * w) {
auto word = s.substr(j - m * w, w);
wd[word] -- ;
if (wd[word] < total[word]) cnt --;
}
auto word = s.substr(j, w);
wd[word] ++;
if (wd[word] <= total[word]) cnt ++ ;
if (cnt == m) res.push_back(j - (m - 1) * w);
}
}
return res;
}
};
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
const findSubstring = function(s, words) {
if (!s || !words || s.length === 0 || words.length === 0) return [];
const counter = new Map();
for (const word of words) {
counter.set(word, (counter.get(word) || 0) + 1);
}
const ans = [];
const sLen = s.length;
const wordsSize = words.length;
const wordLen = words[0].length;
const limit = sLen - wordsSize * wordLen + 1;
for (let i = 0; i < limit; ++i) {
const substr = s.substring(i, i + wordsSize * wordLen);
if (isConcat(substr, counter, wordLen)) {
ans.push(i);
}
}
return ans;
};
function isConcat(candidate, counter, wordLen) {
const seen = new Map();
for (let i = 0; i < candidate.length; i += wordLen) {
const word = candidate.substring(i, i + wordLen);
seen.set(word, (seen.get(word) || 0) + 1);
if (!counter.has(word) || seen.get(word) > counter.get(word)) return false;
}
return true;
}
参考leetcode题解
滑动窗口 + 哈希表
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int n = s.size();
int m = words.size();
if(m == 0) return vector<int>();
int w = words[0].size();
unordered_map<string, int> words_freq;
for(auto& cur: words){
words_freq[cur] ++;
}
int ii, jj, cnt_res;
vector<int> ans;
for(ii = 0; ii < w; ++ ii){
unordered_map<string, int> s_freq;
cnt_res = 0;
for(jj = ii; jj + w <= n; jj += w){
if(ii + m * w <= jj){
string sub_s = s.substr(jj - w * m, w);
if(words_freq.count(sub_s) && s_freq[sub_s] <= words_freq[sub_s]){
cnt_res --;
}
s_freq[sub_s] --;
}
string sub_s = s.substr(jj, w);
if(words_freq.count(sub_s) && s_freq[sub_s] < words_freq[sub_s]){
cnt_res ++;
}
s_freq[sub_s] ++;
if(cnt_res == m){
ans.push_back(jj - (m - 1) * w);
}
}
}
return ans;
}
};
n为字符串s的长度,m为words单词的个数,w为words中每个单词的长度。
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
// 利用两个hash表存储单词出现的次数,再维护一个滑动窗口
Map<String, Integer> allWords = new HashMap<>();
for(String word: words){
allWords.put(word, allWords.getOrDefault(word, 0) + 1);
}
int wordNum = words.length, wordLen = words[0].length();
List<Integer> res = new ArrayList<>();
for(int i = 0;i < s.length() - wordNum * wordLen + 1;i++){
Map<String, Integer> subWords = new HashMap<>();
int index = i;
while(index <i + wordNum * wordLen) {
String curWord = s.substring(index, index + wordLen);
if(!allWords.containsKey(curWord) || allWords.get(curWord).equals(subWords.get(curWord))) {
break;
}
subWords.put(curWord, subWords.getOrDefault(curWord, 0) + 1);
index += wordLen;
}
if(index == i + wordNum * wordLen) {
res.add(i);
}
}
return res;
}
}
将words里的每个word统计频率存放在dict里
然后滑动长度为words字符个数的窗口,比较里面的字符是否在words里
若全部在里面,则增加起始位置到结果列表里面
class Solution:
import collections
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
result = []
words_map = collections.defaultdict(int)
for word in words:
words_map[word] += 1
sLen = len(s)
wordLen = len(words[0])
count = len(words)
wordsLen = wordLen * count
for i in range(sLen - wordLen * count + 1):
cur = s[i : i + wordsLen]
temp = collections.defaultdict(int)
j = 0
for j in range(0, wordsLen + 1, wordLen):
word = cur[j : j + wordLen]
if word not in words_map:
break
temp[word] += 1
if temp[word] > words_map[word]:
break
if j == wordsLen:
result.append(i)
return result
时间复杂度 :O(Nmk)
空间复杂度:O(m)
看题解复现
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
word_len = len(words[0])
cand_len = word_len * len(words)
ans = []
if len(s) < cand_len: return ans
words_occrs = dict()
for word in words:
if word in words_occrs: words_occrs[word] += 1
else: words_occrs[word] = 1
for i in range(len(s)):
if i+cand_len<=len(s):
cand_occrs = dict()
j = i
while j<i+cand_len:
word = s[j:j+word_len]
if word not in words_occrs: break
else:
if word in cand_occrs: cand_occrs[word] += 1
else: cand_occrs[word] = 1
if cand_occrs[word] > words_occrs[word]: break
j+=word_len
if j==(i+cand_len): ans.append(i)
return ans
var findSubstring = function(s, words) {
let dict = new Map()
let len = words[0].length
let count = words.length
let left = 0
const res = []
words.forEach(word => {
dict.set(word, (dict.get(word) || 0) + 1)
})
for (let i = 0; i < s.length - len * count + 1; i++) {
let cur = s.slice(i, i + len * count)
let tmp = new Map()
let j = 0
for (; j < cur.length; j += len) {
let word = cur.slice(j, j + len)
if (!dict.has(word)) {
break
}
tmp.set(word, (tmp.get(word) || 0) + 1)
// 剪枝
if (tmp.get(word) > dict.get(word)) {
break
}
}
if (j === cur.length) {
res.push(i)
}
}
return res
};
n为字符串s的长度,m为words的数组元素个数,k为words内单个word的长度
时间:O(n * m * k)
空间:O(m)
day23 30. 串联所有单词的子串
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words
思路:
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
allwords = collections.Counter(words)
lengthword = len(words[0])
word_num = len(words)
n = len(s)
res = []
for i in range(n-lengthword * word_num + 1):
subwords = collec
tions.defaultdict(int)
index = i
#遍历
while index < i + lengthword*word_num :
curwords = s[index :index +lengthword]
if curwords not in allwords or subwords[curwords] ==allwords[curwords]: #选取的当前字串不在words里面,或者计数已经满了
break
subwords[curwords] += 1
index = index + lengthword
if index == i + lengthword*word_num:
res.append(i)
return res
复杂度分析:
双指针固定滑动窗口,哈希表记录词表频率,每次滑动一个窗口检查是否在哈希表内,若在,哈希表减去频率,继续滑动窗口,直到哈希表为空表示完全匹配上;若不在,移动初始指针
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList();
if(null == s || s.equals ("") || null == words || words.length==0){
return res;
}
//map: <word,count>
Map<String, Integer> map = new HashMap();
for(String word : words){
if(map.containsKey(word)){
int count = map.get(word);
map.put(word, ++count);
}else{
map.put(word, 1);
}
}
Map<String, Integer> mapCopy = new HashMap(map);
int len = words[0].length();
int begin = 0;
while (begin < s.length()){
int i = begin;
int j = i+len;
mapCopy = new HashMap(map);
while(j <= s.length()){
String sub = s.substring(i,j);
if(mapCopy.containsKey(sub)){
int count = mapCopy.get(sub);
if(count > 1){
mapCopy.put(sub, --count);
}else{
mapCopy.remove(sub);
}
i = j;
j = i+len;
if(mapCopy.isEmpty()){
res.add(begin);
break;
}
}else{
break;
}
}
begin++;
}
return res;
}
}
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
word_len = len(words[0])
substr_len = word_len * len(words)
ans = []
if len(s) < substr_len:
return ans
#统计words词频
word_freq = {}
for word in words:
if word in word_freq.keys():
word_freq[word] += 1
else:
word_freq[word] = 1
for i in range(len(s)):
if i + substr_len <= len(s):
substr_freq = {}
j = i
while j < i+substr_len:
word = s[j:j+word_len]
if word not in word_freq.keys():
break
else:
if word in substr_freq.keys():
substr_freq[word] += 1
else:
substr_freq[word] = 1
if substr_freq[word] > word_freq[word]:
break
j += word_len
if j == (i+substr_len):
ans.append(i)
return ans
时间复杂度:O(mnk) 空间复杂度:O(m)
使用哈希表记录单词出现的次数,从s 中截取固定长度的字符串,并添加到哈希表中,使用滑动窗口来判断当前窗口中的字串组成的哈希表是否和words组成的哈希表相等
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
Map<String, Integer> map = new HashMap<>();
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
List<Integer> ans = new ArrayList<>();
int wordLen = words[0].length();
for (int i = 0; i < wordLen; ++i) {
int l = i, r = i, wordCount = 0;
Map<String, Integer> tmp = new HashMap<>();
while (r + wordLen <= s.length()) {
String tmpWord = s.substring(r, r + wordLen);
tmp.put(tmpWord, tmp.getOrDefault(tmpWord, 0) + 1);
++wordCount;
r += wordLen;
while (tmp.getOrDefault(tmpWord, 0) > map.getOrDefault(tmpWord, 0)) {
String leftWord = s.substring(l, l + wordLen);
tmp.put(leftWord, tmp.getOrDefault(leftWord, 0) - 1);
--wordCount;
l += wordLen;
}
if (wordCount == words.length) {
ans.add(l);
}
}
}
return ans;
}
}
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
word_len = len(words[0])
words_len = len(words)
total = word_len * words_len
ans=[]
wordCount = {}
for word in words:
wordCount[word] = wordCount.get(word, 0) + 1
for i in range(len(s) - total + 1):
s1 =s[i : i + total]
subs = [s1[j : j + word_len] for j in range(0, len(s1), word_len)]
if Counter(subs) == wordCount:
ans.append(i)
return ans
遍历子串
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<Integer>();
int wordNum = words.length;
if (wordNum == 0) {
return res;
}
int wordLen = words[0].length();
HashMap<String, Integer> allWords = new HashMap<String, Integer>();
for (String w : words) {
int value = allWords.getOrDefault(w, 0);
allWords.put(w, value + 1);
}
for (int i = 0; i < s.length() - wordNum * wordLen + 1; i++) {
HashMap<String, Integer> hasWords = new HashMap<String, Integer>();
int num = 0;
while (num < wordNum) {
String word = s.substring(i + num * wordLen, i + (num + 1) * wordLen);
if (allWords.containsKey(word)) {
int value = hasWords.getOrDefault(word, 0);
hasWords.put(word, value + 1);
if (hasWords.get(word) > allWords.get(word)) {
break;
}
} else {
break;
}
num++;
}
if (num == wordNum) {
res.add(i);
}
}
return res;
}
}
时间复杂度:O(n*m) 空间复杂度:O(m)
思路很简单,使用滑动窗口,当窗口的大小和words的长度一致时,使用map存储该窗口根据单词长度划分的单词,将map和words构成的map进行对比,满足要求则输出left。
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function (s, words) {
let left = 0,
right = 0;
let slen = s.length;
let wordLen = words[0].length;
let wordNum = words.length;
let wlen = wordNum * wordLen;
let wordMap = new Map();
for (let word of words) {
let count = wordMap.has(word) ? wordMap.get(word) : 0;
wordMap.set(word, count + 1);
}
let res = [];
while (right < slen) {
right++;
if (right - left === wlen) {
if (match(s.substring(left, right), wordMap, wordNum, wordLen)) {
res.push(left);
}
left++;
}
}
return res;
};
function match(str, wordMap, wordNum, wordLen) {
let map = new Map();
for (let i = 0; i < wordNum; i++) {
let word = str.substring(i * wordLen, (i + 1) * wordLen);
let count = map.has(word) ? map.get(word) : 0;
map.set(word, count + 1);
}
let matchflag = true;
for (let [key, value] of wordMap) {
if (!map.has(key) || map.get(key) !== value) {
matchflag = false;
}
}
return matchflag;
}
C++ Code:
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
unordered_map<string, int> allWordsMap;
for (auto& v : words) {
++allWordsMap[v];
}
int num = words.size();
int onelen = words[0].length();
vector<int> res;
if (s.length() < num * onelen) {
return res;
}
for (int left = 0; left < s.length() - num * onelen + 1; ++left) {
unordered_map<string, int> nowWordsMap;
int right = left;
while (right < left + num * onelen) {
auto cur = s.substr(right, onelen);
if (allWordsMap.find(cur) == allWordsMap.end()
|| nowWordsMap[cur] == allWordsMap[cur]) {
break;
}
++nowWordsMap[cur];
right += onelen;
}
if (right == left + num * onelen) {
res.emplace_back(left);
}
}
return res;
}
};
令 n 为数组长度。
使用滑动窗口和两个哈希表,一个哈希表记录words中每个word的个数,一个记录滑动窗口内word已经出现的个数
首先遍历字符串,找到第一个在单词列表中出现的索引位置。根据单词的长度的个数,计算出滑动窗口的大小。在滑动窗口中遍历是否符合每个单词按照要求的个数出现。
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
unordered_map<string, int> mp_index;
unordered_map<string, int> mp_cnt;
for (string &word : words){
mp_index[word] = 0;
mp_cnt[word]++;
}
int word_len = words[0].length();
int left = 0;
int right = left + words.size() * word_len;
while (right <= s.length()){
string word = s.substr(left, word_len);
//找到第一个
if (mp_cnt.count(word)){
for (auto it = mp_index.begin(); it != mp_index.end(); ++it){
it->second = 0;
}
mp_index[word]++;
int i;
for (i = left + word_len; i < right; i += word_len){
string word = s.substr(i, word_len);
if (mp_cnt.count(word) && mp_cnt[word] > mp_index[word]){
mp_index[word]++;
}
else{
break;
}
}
if (i == right){
res.push_back(left);
}
}
++left;
++right;
}
return res;
}
Referring to the solution given. Using a hashmap to record the time each word appear in the list, and using a sliding window of length each word length * number of words to check If the substring meets the requirement. Use another hashmap to store the number of times each word appear in the substring and compare it with the previous hashmap.
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
wlen = len(words[0])
dic = {}
for word in words:
if word not in dic:
dic[word] = 0
dic[word] += 1
count = len(words)
l = len(s)
res = []
for i in range(l - count * wlen + 1):
curr_str = s[i: i + wlen * count]
curr_dic = {}
for j in range(0, len(curr_str), wlen):
word = curr_str[j: j + wlen]
if word not in dic:
break
if word not in curr_dic:
curr_dic[word] = 0
curr_dic[word] += 1
if curr_dic[word] > dic[word]:
break
if dic == curr_dic:
res.append(i)
return res
Time complexity:: O(n) Space complexity: O(n)
先用暴力解一下。使用哈希表统计words的单词频次,打开滑窗,遍历字符串,验证结果。后面可以开n个哈希表存所有遍历可能性(n为words中单词长度),这样就没有了循环嵌套。
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
res = []
size = len(words[0])
check = {}
for j, word in enumerate(words):
if word not in check:
check[word] = 1
else:
check[word] += 1
for i, ch in enumerate(s):
tmp_dic = check.copy()
tmp = i
while True:
if s[tmp:tmp+size] in tmp_dic:
tmp_dic[s[tmp:tmp+size]] -= 1
if tmp_dic[s[tmp:tmp+size]] == 0:
del tmp_dic[s[tmp:tmp+size]]
tmp += size
else:
break
if not tmp_dic:
res.append(i)
return res
时间:O(M^2/N),M为s长度,N为words中word的长度 空间:O(L),L为words长度
建立两个hashmap,将words中的单词存入hashmap1中
遍历s,将s中单词记录到hashmap2中
hashmap2中单词的value与hashmap1中的比较,相同就扫描下一个单词
不同则直接跳出
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
if(words.size() == 0)
return vector<int>{};
int wordsNum = words.size();
int wordsSinglesize = words[0].size();
int wordsToatallen = wordsNum * wordsSinglesize;
unordered_map<string,int> m1;
for(int i = 0;i < wordsNum;i++){
m1[words[i]]++;
}
unordered_map<string,int> m2;
for(int i = 0;(i + wordsToatallen) <= s.size();i++){
int j = 0;
for(j = i;j < (i + wordsToatallen);j =j + wordsSinglesize){
string str0 = s.substr(j,wordsSinglesize);
if(m1[str0] == 0){
break;
}else{
m2[str0]++;
if(m1[str0] < m2[str0])
break;
}
}
if(j == i + wordsToatallen)
res.push_back(i);
m2.clear();
}
return res;
}
};
时间复杂度:O(n*m) n为s的长度,m为words内单词的个数
空间复杂度:O(m)取决于words内单词的个数m
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;
};
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
if (words == null || words.length == 0)
return res;
for (String word : words)
map.put(word, map.getOrDefault(word, 0) + 1);
int sLen = s.length(), wordLen = words[0].length(), count = words.length;
int match = 0;
for (int i = 0; i < sLen - wordLen * count + 1; i++) {
//得到当前窗口字符串
String cur = s.substring(i, i + wordLen * count);
Map<String, Integer> temp = new HashMap<>();
int j = 0;
for (; j < cur.length(); j += wordLen) {
String word = cur.substring(j, j + wordLen);
// 剪枝
if (!map.containsKey(word))
break;
temp.put(word, temp.getOrDefault(word, 0) + 1);
// 剪枝
if (temp.get(word) > map.get(word))
break;
}
if (j == cur.length())
res.add(i);
}
return res;
}
}
var findSubstring = function(s, words) {
let map={};
for(let i=0;i<words.length;i++){
let num=map[words[i]]?map[words[i]]:0;
map[words[i]]=++num;
}
let wordSize=words[0].length;
let len=words.length;
let allSize=wordSize*len;
let res=[];
for(let i=0;i<s.length-allSize+1;i++){
let str=s.substring(i,i+allSize);
let partMap={}
let j=0;
for(;j<str.length;j+=wordSize){
let part=str.substring(j,j+wordSize);
if(!map[part]){
break;
}
let num=partMap[part]?partMap[part]:0;
partMap[part]=++num;
if(partMap[part]>map[part]){
break;
}
}
if(j===str.length){
res.push(i)
}
}
return res;
};
时间复杂度O(n2),空间复杂O(n)
哈希表+滑动窗口
C++ Code:
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
// 设 words中所有单词的长度为 d
int n = s.size(), m = words.size(), d = words[0].size();
int len = 0;
unordered_map<string, int> um;
for (string w : words) {
len += w.size();
um[w]++;
}
// init: 初始化长度为 d 的数组
vector<unordered_map<string, int> > vu(d);
for (int i = 0; i < d && i + len <= n; i++) {
for (int j = i; j < i + len; j += d) {
string w = s.substr(j, d);
vu[i][w]++;
}
if (vu[i] == um) {
res.emplace_back(i);
}
}
// sliding window: 滑动窗口,每次移动 d 个位置
for (int i = d; i + len <= n; i++) {
int r = i % d;
string wa = s.substr(i - d, d), wb = s.substr(i + len - d, d);
if(--vu[r][wa] == 0) vu[r].erase(wa);
vu[r][wb]++;
if (vu[r] == um) {
res.emplace_back(i);
}
}
return res;
}
};
复杂度分析
令 n 为数组长度。
public List<Integer> findSubstring(String s, String[] words) {
if(s == null || s.length() == 0 || words == null || words.length == 0) return Collections.emptyList();
HashMap<String,Integer> map = new HashMap<>(); // map to record frequency of word
int len = words[0].length()*words.length;
for(String str: words){
int count = map.getOrDefault(str,0);
map.put(str,count+1);
}
List<Integer> list = new LinkedList<>();
loop:
for(int i = 0;i<=s.length() - len;i++){ //O(m - n) time complexity, where m is length of s, n is the length of all word
HashMap<String,Integer> tempMap = new HashMap<>(); //
for(int j = 0;j<len;j += words[0].length()){ //O(N) time complexity, where N is length of words
String sub = s.substring(i+j,i+j+words[0].length());
if(map.containsKey(sub)){
int count = tempMap.getOrDefault(sub,0);
tempMap.put(sub,count+1);
}else continue loop;
}
if(map.equals(tempMap)) list.add(i); //O(n) time complexity to compare two maps
}
return list;
}
把string s建立出一个一个为words总长度的滑动窗口,把每一个窗口都分成为words[0]长度的单词,一一加入临时的本窗口有效的map并与原map作比较:1如果不存在可以直接prune;2如果两者的count不同也可以直接prune。最后如果没有被prune则说明当前窗口是符合要求的,加入vector
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if (s.size() == 0 || words.size() == 0) return {};
vector<int> res;
unordered_map<string, int> map;
for (string& s: words) {
map[s]++;
}
int sLen = s.size();
int wordLen = words[0].size();
int wordCount = words.size();
for (int i = 0; i < sLen - wordCount * wordLen + 1; ++i) {
string curr = s.substr(i, wordLen * wordCount);
unordered_map<string, int> temp;
int j = 0;
for (; j < curr.size(); j += wordLen) {
string word = curr.substr(j, wordLen);
if (map.find(word) == map.end()) {
break;
}
temp[word]++;
if (map[word] < temp[word]) {
break;
}
}
if (j == curr.size()) {
res.push_back(i);
}
}
return res;
}
};
30. 串联所有单词的子串
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-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"] 输出:[]