Open azl397985856 opened 2 years ago
题意: words数组中的单词以任意顺序进行拼接, 需要找出每一种可能的拼接结果串在原字符串中是否能找到, 如果能找到, 将其index加入到结果数组中, 否则返回空数组。且子串要与 words
数组中的单词完全匹配,中间不能有其他字符 。
使用两个哈希表: 一个循环外的目标哈希表needWordDict和一个循环内部的哈希表 hasWordsDict。
每次使用一个word长度的窗口去扫描~
与LeetCode 76
用的方法有点像。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
int wordsCount = words.size();
if (wordsCount == 0) return {};
int wordLen = words.front().size();
// 目标哈希表 needWordDict, map: string -> count
unordered_map<string, int> needDict;
for (auto word : words)
{
if (needDict.find(word) != needDict.end())
needDict[word]++;
else needDict[word] = 0;
}
for (int i = 0; i < s.size() - wordLen*wordsCount + 1; i++)
{
int count = 0;
// 构建已有串的哈希表 hasWordsDict
unordered_map<string, int> hasWordsDict;
while (count < wordsCount)
{
string curWord = s.substr(i + count * wordLen, wordLen);
if (needDict.find(curWord) != needDict.end())
{
if (hasWordsDict.find(curWord) != hasWordsDict.end())
hasWordsDict[curWord]++;
else hasWordsDict[curWord] = 0;
}
else break;
if (hasWordsDict[curWord] > needDict[curWord]) break;
count++;
}
if (count == wordsCount) res.push_back(i);
}
return res;
}
};
时间复杂度:(N-K)*K,N 是字符串 s 的长度,K 是单词的长度。
空间复杂度:c, c 是 words 数组的长度。
Not optimal but anyways.
To find a subarray that contains all the words but of an arbitrary order, we can compare their counters. This can be done in brute force, but a O(n^2) solution will not work given n = 10^4.
Now the problem becomes: given an index I, how to find the counter of the next k words efficiently. And this can be done by preprocessing the entire string and store the next k words starting from I in a hashtable. We build the table recursively with memoization, so the preprocessing time is O(n). Space is O(n^2) because we are basically storing a substring for each index.
Once we have the hash table ready, we can check every index and compare the counters of the next k words. The cost for each comparison is O(k) and in total we have O(nk).
Time: O(nk) where n is the length of string and k is the number of words Spaec: O(n^2)
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
counter = Counter(words)
M, W, K = len(s), len(words[0]), len(words)
@lru_cache(None)
def getWords(idx):
if idx >= M: return []
w = s[idx: idx+W]
return [w] + getWords(idx+W)
wsm, ret = {}, []
for i in range(M):
wsm[i] = getWords(i)
for i in range(M):
ws = wsm[i]
c = Counter(ws[:K])
if c == counter:
ret.append(i)
return ret
https://leetcode.com/problems/substring-with-concatenation-of-all-words/
const findSubstring = (s, words) => {
if (!words || words.length === 0) return [];
const m = words.length,
n = words[0].length,
len = m * n,
result = [];
// Build the word-count hash map
const map = {};
for (word of words) map[word] = ~~map[word] + 1;
// Try every possible start position i
for (let i = 0; i < s.length - len + 1; i++) {
// Make a copy of the hash map
const temp = Object.assign({}, map);
for (let j = i; j < i + len; j += n) {
const str = s.substr(j, n);
// Cannot find the word in hash map (words list), try another position
if (!(str in temp)) break;
// All the same word str are found, remove it from the hash map
if (--temp[str] === 0) delete temp[str];
}
// We have gone through the whole s and used all our words in the list
if (Object.keys(temp).length === 0) result.push(i);
}
return result;
};
time: O(mn)? space: O(n)?
滑动窗口+哈希表
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
WordsCounter = collections.Counter(words)
wordNum = len(words)
wordLen = len(words[0])
res = []
for i in range(len(s) - wordNum * wordLen + 1):
WordCounter = collections.defaultdict(int)
index = i
while index < i + wordNum * wordLen:
curWord = s[index: index + wordLen]
if curWord not in WordsCounter or WordCounter[curWord] == WordsCounter[curWord]:
break
WordCounter[curWord] += 1
index += wordLen
if index == i + wordNum * wordLen:
res.append(i)
return res
时间复杂度:O(N * M), N为字符串长度,M为单词个数 空间复杂度:O(N)
# 2 maps
# build counter of word in words
# iterate through s and check every possible window of length total words length
# break if a new word appears in the substring or counter exceeds
# time: O(len(s) * words_length * num_of words)
# space: O(number of unique words)
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
words_length = len(words) * len(words[0])
# not enough chars in s
if words_length > len(s):
return []
counter = {}
res = []
# build counter
for word in words:
counter[word] = counter.get(word, 0) + 1
for i in range(len(s) - words_length + 1):
seen = {}
# increment by single word length
for j in range(i, i + words_length, len(words[0])):
next_word = s[j : j + len(words[0])]
if next_word in counter:
seen[next_word] = seen.get(next_word, 0) + 1
if seen[next_word] > counter[next_word]:
break
else:
break
if seen == counter:
res.append(i)
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 [] all_len = sum(map(len, words)) n = len(s) words = Counter(words) res = [] for i in range(0, n - all_len + 1): tmp = s[i:i+all_len] flag = True for key in words: if words[key] != tmp.count(key): flag = False break if flag:res.append(i) return res
class Solution(object):
def findSubstring(self, s, words):
num_words = len(words)
each_length = len(words[0])
length = num_words * each_length
result = []
def check(segments):
dic = Counter(words)
index = 0
while index<len(segments):
start = index
end = start+each_length
if segments[start:end] in dic and dic[segments[start:end]]>=1:
dic[segments[start:end]]-=1
index=end
else:
return False
return True
for i in range(len(s)):
start = i
end = start + length
if end<=len(s):
segments = s[start:end]
if check(segments):
result.append(start)
return result
public class Solution {
public ArrayList<Integer> findSubstring(String S, String[] L) {
ArrayList<Integer> result = new ArrayList<Integer>();
HashMap<String, Integer> toFind = new HashMap<String, Integer>();
HashMap<String, Integer> found = new HashMap<String, Integer>();
int m = L.length, n = L[0].length();
for (int i = 0; i < m; i ++){
if (!toFind.containsKey(L[i])){
toFind.put(L[i], 1);
}
else{
toFind.put(L[i], toFind.get(L[i]) + 1);
}
}
for (int i = 0; i <= S.length() - n * m; i ++){
found.clear();
int j;
for (j = 0; j < m; j ++){
int k = i + j * n;
String stub = S.substring(k, k + n);
if (!toFind.containsKey(stub)) break;
if(!found.containsKey(stub)){
found.put(stub, 1);
}
else{
found.put(stub, found.get(stub) + 1);
}
if (found.get(stub) > toFind.get(stub)) break;
}
if (j == m) result.add(i);
}
return result;
}
}
复杂度分析
令 n 为数组长度。
哈希表+滑动窗口。用一个哈希表记录各字符出现的位置,用另一个哈希表记录每个单词的个数。 分别从第0,第1... 第l-1个字符开始初始化滑动窗口。移动滑动窗口,得到新单词。 1.若其不在哈希表中,将left移到right处,记录位置的哈希表重置。 2.若其在哈希表中且造成该单词出现次数大于原列表,则把left移动到该单词现在在窗口中第一次出现位置的右边,更新记录位置的哈希表 3.否则,直接更新哈希表 当移动完指针后,若窗口长度与应有长度相等,则得到答案。
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
from collections import defaultdict
d=defaultdict(list)
left=0
right=0
m=len(s)
n=len(words)
l=len(words[0])
cnt=defaultdict(int)
res=[]
for i in words:
d[i]=[]
cnt[i]+=1
for right in range(l):
left=right
for i in words:
d[i]=[]
while right<m:
right+=l
wd=s[right-l:right]
if wd not in d:
left=right
for i in d:
d[i]=[]
elif len(d[wd])>=cnt[wd]:
left=d[wd][0]+l
d[wd].pop(0)
d[wd].append(right-l)
for i in d:
while len(d[i])>0 and d[i][0]<left:
d[i].pop(0)
else:
d[wd].append(right-l)
if right-left==n*l:
res.append(left)
left+=l
return res
时间复杂度:O(m),m为s长度,l为words长度。一共进行了l次长度为m/l的滑动数组
空间复杂度:O(m),存储了每个单词的位置和次数
Explanation
Python
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
d = defaultdict(int)
n = len(words)
wordLength = len(words[0])
for w in words:
d[w] += 1
result = []
for i in range(len(s)-n*wordLength+1): # range: +1
currD = d.copy()
flag = True
for j in range(i, i+n*wordLength, wordLength):
if s[j:j+wordLength] not in currD:
flag = False
break
else:
if currD[s[j:j+wordLength]] == 0:
flag = False
break
else:
currD[s[j:j+wordLength]] -= 1
if flag:
result.append(i)
return result
Complexity:
O(N * m * l)
where N is the length of s, m is the length of the words list and l is the length of each wordO(m)
思路:
n = 字符串s长度 m = 字符串数组words中的字符串个数 k = 字符串数组words中的单个字符串长度
增加判断滑动窗口中的子串是否在给定的字符串数组中(!cnt.count(ss))
复杂度分析: 时间复杂度: O(n m), n为字符串s的长度, m为字符串数组words的size(words.size(),字符串数) 空间复杂度: O(m k), 哈希表空间,m为字符串数组中不同的字符串个数,k为单个字符串的长度
代码(C++):
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int n = s.length();
int m = words.size();
int k = words[0].length();
if (n < m * k) return {};
map<string, int> cnt;
vector<int> res;
for (auto &w : words)
cnt[w]++;
for (int i = 0; i < n - m * k + 1; ++i) {
map<string, int> tmp;
for (int j = 0; j < m; ++j) {
string ss = s.substr(i + j * k, k);
if (!cnt.count(ss)) break;
tmp[ss]++;
}
if (tmp == cnt)
res.push_back(i);
}
return res;
}
};
维护一个固定长度的字符串 slicing window O(N)
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
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
dic = {}
res = []
word_len = len(words[0])
words_len = len(words) * word_len
for word in words:
dic[word] = dic.get(word, 0) + 1
for i in range(len(s) - words_len + 1):
seen = defaultdict(int) # reset seen for every i
for j in range(i, i + words_len, word_len):
word = s[j : j + word_len]
if word in dic:
seen[word] += 1
if seen[word] > dic[word]:
break
if seen == dic:
res.append(i)
return res
Sliding Window
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> result = new ArrayList<>();
int wordPerLen = words[0].length(), offset = 0, numOfWords = words.length;
while (offset < s.length() - wordPerLen * numOfWords + 1){
Map<String, Integer> hasMatched = new HashMap<>();
int numOfMatched = 0;
while (numOfMatched < numOfWords){
String str = s.substring(offset + numOfMatched * wordPerLen, offset + (numOfMatched + 1) * wordPerLen);
if (!map.containsKey(str)) break;
hasMatched.put(str, hasMatched.getOrDefault(str, 0) + 1);
if (hasMatched.get(str) > map.get(str)) break;
numOfMatched++;
}
if (numOfMatched == numOfWords) result.add(offset);
offset++;
}
return result;
}
}
Time Complexity: O(n * L), L is the length of concatenated word, Space Complexity: O(n)
AC
//hashmap
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
HashMap<String, Integer> map = new HashMap<>();
List<Integer> res = new ArrayList<>();
int wordLength = words[0].length();
for(String word: words){
map.put(word, map.getOrDefault(word, 0)+1);
}
HashMap<String, Integer> found = new HashMap<>();
for(int i = 0;i <= s.length() - words.length * wordLength;i++){
found.clear();
int cur = i;
int size = 0;
for(int j = 0;j < words.length;j++){
String str = s.substring(cur, cur + wordLength);
if(map.containsKey(str)){
found.put(str, found.getOrDefault(str, 0)+1);
if(found.get(str) > map.get(str)) break;
size++;
} else {
break;
}
cur = cur + wordLength;
}
if(size == words.length){
res.add(i);
}
}
return res;
}
}
//不可能的words排列组合,还是记录一下backtrack
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<String> res = backtrack(Arrays.asList(words), new ArrayList<>(), "", words.length*words[0].length());
System.out.println(res);
return new ArrayList<>();
}
public List<String> backtrack(List<String> words, List<String> res, String combination, int size){
if(size == combination.length()) {
res.add(combination);
return res;
}
for(int i = 0;i < words.size();i++){
if(words.get(i) == "") continue;
String temp = combination;
String word = words.get(i);
combination += word;
words.set(i, "");
backtrack(words, res, combination, size);
combination = temp;
words.set(i, word);
}
return res;
}
}
time:
space:map所需的空间O(N)
Algo
- Corner: words is empty
- make words a hashmap, key->word, val->times
- window length is the total length of words
- check window one by one
- turn current window into a hashmap
- compare curr_hash & target_hash
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
# corner
if not words: return []
# cal the length of the continuous sub-str
word_len = len(words[0])
sub_len = len(words) * word_len
# set words to a hash set, key->word, val->times
target_hash = collections.defaultdict(int)
for word in words: target_hash[word] += 1
# arr to save idx
res = []
# fixed sliding window
slow = 0
fast = sub_len
while fast != len(s) + 1:
# get curr window
curr = s[slow:fast]
# transfer curr to a hashmap
window_words = collections.defaultdict(int)
cut = 0
while cut != sub_len:
window_words[curr[cut:cut + word_len]] += 1
cut += word_len
# compare words and curr
wrong = False
for key in target_hash.keys():
if target_hash[key] != window_words[key]: wrong = True
if wrong: break
if not wrong: res.append(slow)
# update slow & fast
slow += 1
fast += 1
return res
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/
HashMap + 双指针,slidding window的思想。 map里存储单词和对应频率。i是str的开始idx,j每次移动一个单词的长度的距离,然后看str(i, j)是否存在于map中并且频率是否 >= 1,是的话就将map频率减1(copy of map),然后继续移动j指针。使用一个k来记录匹配到的word的数量,如果变为0时就记录下当前i的idx,然后iterate到s.length() - words.length * words[0].length()结束。
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
if(s == null || words == null) return new ArrayList<>();
List<Integer> res = new ArrayList<>();
final int n = words.length;
final int m = words[0].length();
Map<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++) {
Map<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;
}
}
O(n^2)
O(n)
思路:首先创建一个对于words的counter, 然后创建一个固定大小的sliding window, sliding window的长度为words里的所有单词能组成的子串的长度,然后用这个滑动窗口来遍历s,每次遍历找到可能的子串时再拆成单词表,用counter和words的counter比较
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
wordlen = len(words[0])
substrlen = wordlen * len(words)
count_1 = Counter(words)
ans = []
for i in range(0, len(s) - substrlen + 1):
substr = s[i: i + substrlen]
word_list = []
for j in range(0, substrlen, wordlen):
word_list.append(substr[j: j+wordlen])
count_2 = Counter(word_list)
if count_1 == count_2:
ans.append(i)
return ans
Time Complexity: O(len(s) len(words)len(words[0])) Space Complexity: O(len(words))
idea: scan the whole string for substring with length of unit word length * word number. Since each word has the same length, we can break the substring into parts with equal size. Use a hashmap to record each part and its frequency. Use another hashmap to record the same info of the original word array. Compare these two hashmap, if they are equal, save the index.
Time: O(mnk)
Space: O(m)
m is the # of words in the array, k is the unit length of word
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
int unitLen = words[0].length();
int substringLen = unitLen * words.length;
HashMap<String, Integer> wordMap = new HashMap<>();
ArrayList<Integer> res = new ArrayList<>();
for(String word: words) {
wordMap.put(word,wordMap.getOrDefault(word,0)+1);
}
for(int i=0;i+substringLen<=s.length();i++) {
String substring=s.substring(i,i+substringLen);
HashMap<String, Integer> substrMap = new HashMap<>();
for(int j=0;j<substring.length();j=j+unitLen) {
String part = substring.substring(j,j+unitLen);
substrMap.put(part,substrMap.getOrDefault(part,0)+1);
}
if(wordMap.equals(substrMap))
res.add(i);
}
return res;
}
}
写了一个有点复杂, 效率还可以
fun findSubstring(s: String, wordsArray: Array<String>): List<Int> {
val w = wordsArray[0].length
if (s.length < w) return emptyList()
val map = LinkedHashMap<Int, String>()
val freq = wordsArray.asSequence().groupingBy { it }.eachCount()
for (i in 0..s.length - w) {
val sub = s.substring(i, i + w)
if (freq.contains(sub)) map[i] = sub
}
val res = mutableListOf<Int>()
map.keys.forEach key@{ start ->
val f: MutableMap<String, Int> = freq.toMutableMap()
var index = start
repeat(wordsArray.size) {
map[index]?.let { word ->
f[word]?.let {
f[word] = it - 1
if (it == 1) f.remove(word)
} ?: return@key
} ?: return@key
index += w
}
res.add(start)
}
return res
}
可能Java写的更简单些
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
if (s == null || s.length() == 0 || words == null || words.length == 0) {
return res;
}
int total = words.length;
int len = words[0].length();
Map<String, Integer> ptn = new HashMap<>();
for (String word : words) {
ptn.compute(word, (k, v) -> v == null ? 1 : v + 1);
}
Map<String, Integer> found = new HashMap<>();
for (int i = 0; i <= s.length() - total * len; i++) {
found.clear();
int cnt = 0;
while (cnt < total) {
String t = s.substring(i + cnt * len, i + (cnt + 1) * len);
if (!ptn.containsKey(t)) break;
found.compute(t, (k, v) -> v == null ? 1 : v + 1);
if (found.get(t) > ptn.get(t)) break;
cnt++;
}
if (cnt == total) res.add(i);
}
return res;
}
}
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
n, wl = len(words), len(words[0])
w_dict, res = {}, []
for w in words:
w_dict[w] = w_dict.get(w, 0) + 1
for i in range(0, len(s) - n * wl + 1):
tmp_dict = w_dict.copy()
match, j = 0, i
while match < n:
w = s[j : j + wl]
if w not in tmp_dict or tmp_dict[w] < 1:
break
match += 1
tmp_dict[w] -= 1
j += wl
if match == n:
res.append(i)
return res
Time complexity: O(LMN) L=len(s), M=len(words), N=len(words[0])
Space complexity: O(M)
from collections import Counter
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
word_dict = Counter(words)
word_len = len(words[0])
total_len = len(words) * word_len
res = []
for i in range(len(s) - total_len + 1):
sub_string = s[i:i+total_len]
t_dict = word_dict.copy()
sub_list = [sub_string[j:j+word_len] for j in range(0, total_len, word_len)]
sub_dict = Counter(sub_list)
if t_dict == sub_dict:
res.append(i)
return res
words
words
words
, and create a dictionary for this substringTime: O(KMN), where M is the size of words
and K is the length of each word
Space: O(N)
def findSubstring(self, s: str, words: List[str]) -> List[int]:
allWords = collections.Counter(words)
wordNum = len(words)
wordLen = len(words[0])
res = []
for i in range(len(s) - wordNum * wordLen + 1):
subWords = collections.defaultdict(int)
index = i
while index < i + wordNum * wordLen:
curWord = s[index: index + wordLen]
if curWord not in allWords or subWords[curWord] == allWords[curWord]:
break
subWords[curWord] += 1
index += wordLen
if index == i + wordNum * wordLen:
res.append(i)
return res
python
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
# method: 双指针和固定长度滑动窗口
ans = []
words.sort()
len1word = len(words[0])
lenslidewin = len1word * len(words)
l,r = 0, lenslidewin-1
while r <= len(s)-1:
tempword = s[l:r+1]
templst = []
segpointer = l
#分割字符串为list
while segpointer <= r:
templst.append(s[segpointer:segpointer+len1word])
segpointer += len1word
templst.sort()
if templst == words:
ans.append(l)
r += 1
l += 1
return ans
time complexity: O(n*len1word*wc);
n: length of s; len1word: length of one word in words; wc: word count, the length of words;
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
// 记录words中所有的单词到targetWords中 && 记录words中所有单词的总长度
HashMap<String, Integer> targetWords = new HashMap<>();
int sum = 0;
for (String w: words) {
targetWords.put(w, targetWords.getOrDefault(w, 0) + 1);
sum += w.length();
}
// s中每个字母都作为窗口滑动的起点
int left, right, wordLen, len, valid;
List<Integer> res = new ArrayList<>();
HashMap<String, Integer> visitedWords = new HashMap<>();
HashSet<Integer> visitedLeft = new HashSet<>();
wordLen = words[0].length();
len = s.length();
for (int i = 0; i + sum <= len; i ++) {
valid = 0;
right = left = i;
visitedWords.clear();
if (visitedLeft.contains(left)) continue;
while (right + wordLen <= len){
// 判断当前位置的下一个单词是否在words中
String curStr = s.substring(right, right+wordLen);
if (!targetWords.containsKey(curStr)) break;
// 窗口扩大
visitedWords.put(curStr, visitedWords.getOrDefault(curStr, 0) + 1);
if (visitedWords.get(curStr).equals(targetWords.get(curStr))) valid ++;
right += wordLen;
// 窗口缩小
while (visitedWords.get(curStr).compareTo(targetWords.get(curStr)) > 0) {
String leftStr = s.substring(left, left + wordLen);
if (visitedWords.get(leftStr).equals(targetWords.get(leftStr))) valid --;
if (visitedWords.get(leftStr) == 1) visitedWords.remove(leftStr);
else visitedWords.put(leftStr, visitedWords.get(leftStr) - 1);
left += wordLen;
visitedLeft.add(left);
}
// 记录left
if (valid == targetWords.size()) res.add(left);
}
}
return res;
}
}
关键信息是要找的单词长度是相同的 固定长度滑动窗口 + 窗口中用哈希表计数,窗口每走一步,都要统计窗口中出现的单词个数是否和words完全一致。
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
const wordCounters = new Map();
for (const word of words) {
const counter = wordCounters.get(word) || 0;
wordCounters.set(word, counter + 1);
}
// sliding window with fixed length
const ans = [];
const windowLength = words.length * words[0].length;
for (let i=0; i<=s.length - windowLength; i++) {
const newCounters = new Map(wordCounters);
let isMatch = true;
for (let j=i; j<i+windowLength; j+=words[0].length) {
const word = s.substring(j, j+words[0].length);
const counter = newCounters.get(word) || 0;
if (counter > 0) {
newCounters.set(word, counter - 1);
} else {
// not match
isMatch = false;
break;
}
}
if (isMatch) {
ans.push(i);
}
}
return ans;
};
复杂度分析
利用题目说的words长度相同的条件,每次遍历考虑判断总长度和模式字符串相同的子字符串。判断子字符串和模式字符串的hash相同(但感觉复杂度可能可以降低,看了大佬的解答,好像比较难优化了)
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
# hash
target_hash = dict()
for w in words:
target_hash[w] = target_hash.get(w, 0) + 1
target_length = len(words[0]) * len(words)
def str_hash(s):
hashmap = dict()
i = 0
while i <= len(s)-len(words[0]):
# 字符串bba 匹配['bb', 'ba']? 看上去限制了目标长度不会碰到这种问题了,仅仅count会有问题,但是子串长度限定之后,hashmap和目标hashmap必须匹配的条件下就不会有问题
if s[i:i+len(words[0])] in target_hash:
hashmap[s[i:i+len(words[0])]] = hashmap.get(s[i:i+len(words[0])], 0) + 1
i += len(words[0])
else:
i += 1
return hashmap
res = []
for i in range(len(s)):
if i + target_length > len(s):
break
cur_str = s[i:i+target_length]
cur_hash = str_hash(cur_str)
if cur_hash == target_hash:
res.append(i)
return res
C++ Code:
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> ret;
unordered_map<string, int> record;
int oneWordSize = words[0].size();
for(int i =0; i < words.size(); i++)
record[words[i]]++;
if(s.size()< words.size() * oneWordSize)
return ret;
for(int i =0; i<=s.size() - words.size() * oneWordSize; i++)
{
int count =0;
unordered_map<string, int> record2;
if(dfs(s, record, i, oneWordSize, words.size(), record2, count))
ret.push_back(i);
}
return ret;
}
bool dfs(string& s, unordered_map<string,int>& record, int root, int oneWordSize, int wordSize, unordered_map<string,int>& record2, int count)
{
if(root>=s.size())
return false;
string tmp = s.substr(root, oneWordSize);
if(record.find(tmp)==record.end())
return false;
else
{
record2[tmp]++;
if(record2[tmp]> record[tmp])
return false;
}
count++;
if(count == wordSize)
return true;
return dfs(s, record, root+oneWordSize, oneWordSize, wordSize, record2, count);
}
};
把words全部储存在哈希表里,遍历s,依次删除哈希表中的值
public List<Integer> findSubstring(String s, String[] words) {
Map<String,Integer> map1= new HashMap<>();
int l=0;
int sum= 0;
for (String word :
words) {
map1.put(word,map1.getOrDefault(word,0)+1);
l=word.length();
sum=sum+l;
}
Map<String, Integer> map2 = new HashMap<>(map1);
int start ;
int end ;
List<Integer> list = new ArrayList<>();
for (int i = 0; i < s.length(); i++) {
start =i;
end =i + l;
while (end<=i+sum&&end<=s.length()){
String substring = s.substring(start, end);
start= end;
end = end+l;
if(map2.containsKey(substring)){
if(map2.get(substring)-1>0){
map2.put(substring,map2.get(substring)-1);
}else {
map2.remove(substring);
}
}else {
break;
}
}
if(map2.size()==0){
list.add(i);
}
map2 = new HashMap<>(map1);
}
return list;
}
T:O(S.length()*words.size()) T: O(S.length())
func findSubstring(s string, words []string) []int {
res := []int{}
counter := map[string]int{}
for _, w := range words {
counter[w]++
}
length, totalLen, tempCounter := len(words[0]), len(words[0])*len(words), copyMap(counter)
for i, start := 0,0; i < len(s) - length + 1 && start < len(s) - length + 1; i++{
if (tempCounter[s[i:i+length]] > 0) {
tempCounter[s[i:i+length]]--
if checkWords(tempCounter) && (i+length-start == totalLen) {
res = append(res, start)
continue
}
i = i + length - 1
} else {
start++
i = start - 1
tempCounter = copyMap(counter)
}
}
return res
}
func checkWords(s map[string]int) bool {
flag := true
for _, v := range s {
if v > 0 {
flag = false
break
}
}
return flag
}
func copyMap(s map[string]int) map[string]int {
c := map[string]int{}
for k, v := range s {
c[k] = v
}
return c
}
很明显的滑动窗口,即type3固定间距指针 我的想法是在窗口内看看包不包含所有的内容。 没想到hash表一开始,参考了答案
但是hash的话是用字符还是每个string呢?-》 当然是单词啦 有些纠结怎么比较,其实在外面iterate好word以后里面存到字典直接比数字也ok啦。 Python方面老是忘记怎么写getOrDefault = wordDict[word] = wordDict.get(word,0) + 1
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
l = 0
k = len(words)
n = len(words[0])
wordDict = {}
result = []
for w in words:
wordDict[w] = wordDict.get(w,0) + 1
# wordDict[w] += 1
index = 0
while l+k*n<= len(s):
# split
split_sub = [str(s[index : index + n]) for index in range(l,l+k*n, n)]
diction = {}
j = 0
while j<k:
# put or +1
sub = split_sub[j]
diction[sub] = diction.get(sub,0) + 1
if diction.get(sub) > wordDict.get(sub):
break
j = j + 1
if(j==k):
result.append(l)
l = l + 1
return result
哈希表。建立初始哈希表储存串联所有单词的子串的单词和对应出现次数。遍历字符串s
确定子串起始点。每次先复制初始的哈希表,然后从起始点开始截取长度为words[i].length
的子字符串,看该子字符串是否出现在哈希表中。如果没出现,则将起始点后移一位。如果出现,更新哈希表,出现次数减1,次数为0时则将Key从哈希表中删除。重复次循环继续截取长度为words[i].length
的子字符串,直到哈希表为空。如果循环结束哈希表为空,表示子串中所有单词全部找到,将起始点存放到结果数组中,将起始点后移一位继续遍历。最后返回结果数组。
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
int slen = s.length();
int num = words.length;
int wlen = words[0].length();
if (slen < num * wlen) return res;
Map<String, Integer> wordMap = new HashMap<>();
for (String word: words) {
wordMap.put(word, wordMap.getOrDefault(word, 0) + 1);
}
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i <= slen - num * wlen; i++) {
String sub = "";
int start = i;
map.putAll(wordMap);
do {
sub = s.substring(start, start + wlen);
if (!map.containsKey(sub)) break;
int count = map.get(sub) - 1;
if (count == 0) map.remove(sub);
else map.put(sub, count);
start += wlen;
} while (!map.isEmpty());
if (map.isEmpty()) res.add(i);
map.clear();
}
return res;
}
}
复杂度分析
s
的长度,m为words
的长度,l为每个单词的长度。如果将substring()
函数操作数定为1,则为O(mn)。如果将substring()
函数操作数定为l,则为O(mnl)暴力求解 遍历字符串s 判断是否满足
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new LinkedList<>();
int len = words[0].length();
for(int i=0;i<=s.length()-len*words.length;i++){
HashMap<String,Integer> map = new HashMap<>();
for(int k =0;k<words.length;k++){
map.put(words[k],map.getOrDefault(words[k],0)+1);
}
int x=0;
for(int j=0;j<words.length;j++){
String cur_str = s.substring(i+j*len,i+(j+1)*len);
if(map.containsKey(cur_str)){
if(map.getOrDefault(cur_str,0)!=0) x++;
map.put(cur_str,map.getOrDefault(cur_str,0)-1);
}else{
break;
}
}
if(x==words.length){
res.add(i);
}
}
return res;
}
}
时间复杂度:O(n*m)n为s长度 m为words长度 空间复杂度:O(m)m为words长度
滑动窗口+哈希表
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
# time n*m
# space n
# 思路一
# 双哈希表
# 第一个哈希表用于存储words中单词出现的频次
# 第二个哈希表用于存储遍历过程中单词出现的频次
allWords = collections.Counter(words)
lengthWord = len(words[0])
numOfWords = len(words)
n = len(s)
res = []
for i in range(0, n - lengthWord * numOfWords + 1):
subWords = collections.defaultdict(int)
idx = i
# 开始检查子串内部是否满足条件
while idx < i + lengthWord * numOfWords:
curWord = s[idx: idx+lengthWord]
# 如果不存在或者存在的次数已经满了
if curWord not in allWords or subWords[curWord] == allWords[curWord]:
break
subWords[curWord] += 1
idx = idx + lengthWord
# 此时如果idx位于末尾表示满足条件 即没有被break过
if idx == i + lengthWord * numOfWords:
res.append(i)
return res
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);
}
//将所有移动分成 wordLen 类情况
for (int j = 0; j < wordLen; j++) {
HashMap<String, Integer> hasWords = new HashMap<String, Integer>();
int num = 0; //记录当前 HashMap2(这里的 hasWords 变量)中有多少个单词
//每次移动一个单词长度
for (int i = j; i < s.length() - wordNum * wordLen + 1; i = i + wordLen) {
boolean hasRemoved = false; //防止情况三移除后,情况一继续移除
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)) {
// hasWords.put(word, value);
hasRemoved = true;
int removeNum = 0;
//一直移除单词,直到次数符合了
while (hasWords.get(word) > allWords.get(word)) {
String firstWord = s.substring(i + removeNum * wordLen, i + (removeNum + 1) * wordLen);
int v = hasWords.get(firstWord);
hasWords.put(firstWord, v - 1);
removeNum++;
}
num = num - removeNum + 1; //加 1 是因为我们把当前单词加入到了 HashMap 2 中
i = i + (removeNum - 1) * wordLen; //这里依旧是考虑到了最外层的 for 循环,看情况二的解释
break;
}
//出现情况二,遇到了不匹配的单词,直接将 i 移动到该单词的后边(但其实这里
//只是移动到了出现问题单词的地方,因为最外层有 for 循环, i 还会移动一个单词
//然后刚好就移动到了单词后边)
} else {
hasWords.clear();
i = i + num * wordLen;
num = 0;
break;
}
num++;
}
if (num == wordNum) {
res.add(i);
}
//出现情况一,子串完全匹配,我们将上一个子串的第一个单词从 HashMap2 中移除
if (num > 0 && !hasRemoved) {
String firstWord = s.substring(i, i + wordLen);
int v = hasWords.get(firstWord);
hasWords.put(firstWord, v - 1);
num = num - 1;
}
}
}
return res;
}
}
Language: Java
public List<Integer> findSubstring(String s, String[] words) {
Map<String, Integer> counts = new HashMap<>();
for (String word : words) {
counts.put(word, counts.getOrDefault(word, 0) + 1);
}
List<Integer> result = new ArrayList<>();
int wordsCount = words.length;
int wordLength = words[0].length();
for (int i = 0; i < s.length() - wordsCount * wordLength + 1; i++) {
Map<String, Integer> seen = new HashMap<>();
int j = 0;
while (j < wordsCount) {
String word = s.substring(i + j * wordLength, i + (j + 1) * wordLength);
if (counts.containsKey(word)) {
seen.put(word, seen.getOrDefault(word, 0) + 1);
if (seen.get(word) > counts.getOrDefault(word, 0)) {
break;
}
} else {
break;
}
j++;
}
if (j == wordsCount) {
result.add(i);
}
}
return result;
}
brute-force
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
lens = len(words[0])
total = lens * len(words)
if len(s) < total:
return []
ans = []
#print(len(s),total)
dic1 = {}
for word in words:
if word in dic1:
dic1[word] += 1
else:
dic1[word] =0
for i in range(len(s)-total+1):
allin = 0
for w in words:
if w in s[i:i+total]:
allin +=1
else:
#slow += 1
break
if allin == len(words):
ans.append(i)
refine_ans = []
for start in ans:
temp = s[start:start+total]
dic2 = {}
for i in range(0,total,lens):
print(temp[i:i+lens])
if temp[i:i+lens] in dic2:
dic2[temp[i:i+lens]] += 1
else:
dic2[temp[i:i+lens]] =0
good = True
for k,v in dic1.items():
if k in dic2:
if dic2[k] == v:
pass
else:
good = False
else:
good = False
if good:
refine_ans.append(start)
return refine_ans
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
word_map = Counter(words)
results = []
word_size = len(words[0])
num_word = len(words)
list_size = word_size * num_word
for i in range(len(s) - list_size + 1):
seen = dict(word_map)
word_used = 0
for j in range(i, i + list_size, word_size):
sub_str = s[j: j + word_size]
if sub_str in seen and seen[sub_str] > 0:
seen[sub_str] -= 1
word_used += 1
else:
break
if word_used == num_word:
results.append(i)
return results
HashMap。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
unordered_map<string, int> hashMapWords;
vector<int> res;
if (s.size() == 0 || words.size() == 0) return res;
for (int i = 0; i < words.size(); i++) hashMapWords[words[i]]++;
for (int i = 0; i < s.size() - words[0].size() * words.size() + 1; i++) {
string tempWord = s.substr(i, words[0].size() * words.size());
unordered_map<string, int> hashMapTemp;
int j;
for (j = 0; j < tempWord.size(); j += words[0].size()) {
string subTempWord = tempWord.substr(j, words[0].size());
if (!hashMapWords.count(subTempWord)) break;
hashMapTemp[subTempWord]++;
if (hashMapTemp[subTempWord] > hashMapWords[subTempWord]) break;
}
if (j == tempWord.size()) res.push_back(i);
}
return res;
}
};
another sliding window problem. Pay attention to shrinking and updating and condition: shrinking: when match == Len(needs hashmap) updating result: when right - left = word_len * wordlist_len
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
needs = {}
for word in words:
if word in needs:
needs[word] += 1
else:
needs[word] = 1
n = len(s)
# pay attention that word in the words list are of the same length
m = len(words[0])
size = len(words)
if size == 0:
return;
ans = []
for i in range(m):
left = i
right = i
valid = 0
# ans = []
windows = {}
while right <= n-m:
wr = s[right: right+m]
if wr in needs:
if wr in windows:
windows[wr] += 1
else:
windows[wr] = 1
if windows[wr] == needs[wr]:
valid += 1
right += m
while valid == len(needs):
wl = s[left: left+m]
print("right:", right)
print("left:", left)
# pay attention to ending condition
if right - left == size * m:
ans.append(left)
if wl in needs:
windows[wl] -= 1
if windows[wl] < needs[wl]:
valid -= 1
left += m
return ans
思路
由于是乱序,那么s子串和words的串里面每一个word数量一样就行
所以按照这个思路:设置窗口大小为words总长,移动窗口计算s子串中各个word的数量
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
Map<String, Integer> map = new HashMap<>();
List<Integer> res = new ArrayList<>();
if(words == null){
return res;
}
if(words.length == 0){
return res;
}
for(String word :words){
map.put(word, map.getOrDefault(word, 0)+1);
}
int sLen = s.length();
int wordLen = words[0].length();
int cnt = words.length;
for(int i = 0; i < sLen - wordLen*cnt +1; i++){
String childstr = s.substring(i, i + wordLen*cnt);
Map<String, Integer> smap = new HashMap<>();
int loc = 0;
for(; loc < childstr.length(); loc += wordLen){
String word = childstr.substring(loc, loc+ wordLen);
if(!map.containsKey(word))break;
smap.put(word, smap.getOrDefault(word, 0)+1);
if(smap.get(word)>map.get(word))break;
}
if(loc == childstr.length()){
res.add(i);
}
}
return res;
}
}
时间复杂度:当长度很短时,s视为每个字符都要遍历,为s的长度,在第二层中每次都要遍历words和word,所以复杂度为O(N M K),N为S长度,M为Words长度,K为每个word长度。
空间复杂度:O(M)
双哈希表+滑动窗口 用一个哈希表存储words 另一个哈希表存储滑动窗口内的单词
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> ans;
unordered_map<string,int>b;
int len=s.size();
for(auto x:words)
b[x]++;
int wordlen=words[0].size();
for(int m=0;m<wordlen;m++)
{
unordered_map<string,int>a;
int i=m,j=m;
for(int k=0;k<words.size();k++)
{
a[s.substr(j,wordlen)]++;
j+=wordlen;
}
while(j<len)
{
if(a==b)
ans.push_back(i);
a[s.substr(i,wordlen)]--;
if(a[s.substr(i,wordlen)]==0)
a.erase(s.substr(i,wordlen));
a[s.substr(j,wordlen)]++;
i+=wordlen;
j+=wordlen;
}
if(a==b)
ans.push_back(i);
}
return ans;
}
};
复杂度分析
var findSubstring = function(s, words) {
const charLens = words[0].length;
const totalWordsLens = charLens * words.length;
const storage = {};
const res = [];
words.forEach(e=> storage[e] = (storage[e] || 0)+1);
for(let i = 0; i<=s.length-totalWordsLens;i++){
// coppy the origin storage
let coppyStorage = {...storage};
let count = words.length;
// console.log("outer:",coppyStorage,storage,count)
for(let j = i; j< j+totalWordsLens; j+= charLens){
let currChar = s.slice(j,j+charLens);
// console.log(i,j,currChar)
if( !(currChar in coppyStorage) || coppyStorage[currChar]<= 0 ){
break;
}
coppyStorage[currChar]--;
count--;
// console.log("inner:",coppyStorage,storage,count)
}
if(count === 0){
res.push(i);
}
}
return res;
};
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
res, ct = [], collections.defaultdict(int)
word_num = len(words)
s_len = len(s)
if words != []:
word_len = len(words[0])
else:
return res
for i in range(word_num):
ct[words[i]] += 1
for i in range(s_len-word_num*word_len+1):
cur, j = collections.defaultdict(int), 0
while j<word_num:
word = s[i+j*word_len:i+(j+1)*word_len]
if not word in ct:
break
cur[word] += 1
if cur[word] > ct[word]:
break
j += 1
if j == word_num:
res.append(i)
return res
Hash table and Two pointers
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
memo = collections.defaultdict(lambda:0)
n = len(words[0])
m = len(words)
res = []
if len(s) < m*n : return []
for word in words:
memo[word] += 1
for i in range(len(s) - n*m + 1):
curr = s[i:i+m*n]
temp = collections.defaultdict(lambda:0)
j = 0
while (j < m*n):
sub_curr = curr[j:j+n]
if sub_curr not in memo: break
temp[sub_curr]+=1
if temp[sub_curr] >memo[sub_curr]: break
j += n
if j == m*n : res.append(i)
return res
Space: O(M) Time: O( S M N)
M is number of words, N is the length of each word
import copy
class Solution:
def findSubstring(self, s: str, words):
''' sliding window
由于 words 里面的 word 等长
那么 words 的长 m 以及 word 的长 n 可以组成 m * n
用这个 window 在 s 中遍历查找即可
'''
m = len(words)
n = len(words[0])
if len(s) < m * n:
return []
words_count = {}
for w in words:
words_count[w] = words_count.get(w, 0) + 1
res = []
i = 0
while i + m*n <= len(s):
tmp = s[i:i+m*n]
# 注意此处必须是深拷贝
tmp_words = copy.deepcopy(words_count)
j = 0
while j < len(tmp) and tmp[j*n:(j+1)*n] in tmp_words:
tmp_words[tmp[j*n:(j+1)*n]] -= 1
j += 1
counts = tmp_words.values()
# 注意此处判断条件必须有这两个,才能保证 counts 里面有且只有 0
if sum(counts) == 0 and len(set(counts)) == 1:
res.append(i)
i += 1
return res
s = Solution()
s.findSubstring('barfoothefoobarman', ["foo","bar"])
s.findSubstring("wordgoodgoodgoodbestword", ["word","good","best","word"])
暴力就是从前往后遍历,用一个dict来存word出现次数,每次check word是不是在words里面,并且word的出现次数要小于等于在words里面的个数 否则就跳到下一个,
可以通过two pointers 来优化
from typing import List
from collections import Counter, defaultdict
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
"""
brute force
"""
word_count_dict = Counter(words)
word_length = len(words[0])
word_num = len(words)
result = []
for i in range(0, len(s) - word_length * word_num + 1):
actual_word_count = defaultdict(int)
end = i
find_word_num = 0
while end < len(s):
word = s[end : end + word_length]
if (
word in word_count_dict
and actual_word_count[word] < word_count_dict[word]
and find_word_num < word_num
):
actual_word_count[word] += 1
find_word_num += 1
else:
break
end += word_length
if find_word_num == word_num:
result.append(i)
return result
def findSubstring(self, s: str, words: List[str]) -> List[int]:
"""
two pointers
"""
word_count_dict = Counter(words)
word_length = len(words[0])
word_num = len(words)
result = []
for i in range(word_length):
start, end = i, i
actual_word_count = defaultdict(int)
while end < len(s):
word = s[end : end + word_length]
while (
word in word_count_dict
and word_count_dict[word] <= actual_word_count[word]
):
actual_word_count[s[start : start + word_length]] -= 1
start += word_length
if (
word in word_count_dict
and actual_word_count[word] < word_count_dict[word]
):
actual_word_count[word] += 1
elif word not in word_count_dict:
start = end + word_length
actual_word_count = defaultdict(int)
end += word_length
if end - start == word_num * word_length:
result.append(start)
return result
time O(len(s)len(words[0])len(words))
space O(len(words)*len(words[0]))
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
Map<String, Integer> wordMap = new HashMap<>();
List<Integer> result = new ArrayList<>();
for (String word: words){
wordMap.put(word, wordMap.getOrDefault(word, 0) + 1);
}
int wordNum = words.length, wordLen = words[0].length();
for (int i = 0; i + wordNum * wordLen - 1 < s.length(); i++){
Map<String, Integer> winMap = new HashMap<>();
int index = i;
while (index < i + wordNum * wordLen){
String str = s.substring(index, index+ wordLen);
if (!wordMap.containsKey(str) || winMap.getOrDefault(str, 0) == wordMap.get(str)){
break;
}
winMap.put(str, winMap.getOrDefault(str, 0) + 1);
index += wordLen;
}
if (index == i + wordNum * wordLen){
result.add(i);
}
}
return result;
}
}
代码性能似乎一般,初学滑动窗口,为了降低学习曲线就优化到这吧
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
Map<String, Integer> word2Count = new HashMap<>();
for (String word : words) {
word2Count.put(word, word2Count.getOrDefault(word, 0) + 1);
}
List<Integer> res = new ArrayList<>();
int len = s.length(), wordLen = words[0].length(), wordsLen = wordLen * (words.length);
if (s.length() < wordsLen) {
return res;
}
int left = 0, right = 0;
for (int i = 0; i < wordLen; i++) {
left = right = i;
int count = words.length;
Map<String, Integer> window = new HashMap<>();
while (right + wordLen <= len) {
String temp = s.substring(right, right + wordLen);
// 如果不包含此单词,直接窗口左移
if (!word2Count.containsKey(temp)) {
right += wordLen;
left = right;
window.clear();
count = words.length;
} else {
window.put(temp, window.getOrDefault(temp, 0) + 1);
count--;
while (word2Count.get(temp) < window.get(temp)) {
String w = s.substring(left, left + wordLen);
window.put(w, window.get(w) - 1);
count++;
left += wordLen;
}
right += wordLen;
}
if (count == 0) {
res.add(left);
}
}
}
return res;
}
}
【思路】滑动窗口,没有来得及优化。 就是固定words.length * words[0].length() 大小的窗口,每看一个窗口,就copy一个存入每个词count的hashmap(因为有可能一个词出现多次)。 这个题目最tricky的地方就是在于查看每个词,hashmap里面count--,如果hashmap为空,就找到了,return i。 比较难想到判断hashmap为空。之前老想着两个hashmap相等,但是就太复杂了
【复杂度】O(s.length words.length words[0].length)
public List<Integer> findSubstring(String s, String[] words){
List<Integer> ans = new ArrayList<>();
if(s == null || words == null || s.length() ==0||words.length ==0) return ans;
int word_length = words[0].length();
HashMap<String,Integer> word_count = new HashMap<>();
for(int i = 0; i<words.length; i++){
int count = word_count.getOrDefault(words[i],0);
word_count.put(words[i], count+1);
}
int total_length = word_length * words.length;
for(int i = 0; i<=s.length()-total_length; i+=1){
String current = s.substring(i, i+total_length);
HashMap<String,Integer> current_count = new HashMap<String, Integer>(word_count);
for(int j= 0; j<words.length;j+=1){
String temp = current.substring(j*word_length,(j+1)*word_length);
if(!current_count.containsKey(temp)) break;
int count = current_count.get(temp);
if(count == 1){
current_count.remove(temp);
}else{
current_count.put(temp, count-1);
}
if(current_count.isEmpty()){
ans.add(i);
}
}
}
return ans;
}
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"] 输出:[]