Closed azl397985856 closed 2 years ago
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
for(String word: words){
map.put(word, map.getOrDefault(word,0) + 1);
}
int n = s.length();
int wordSize = words[0].length();
int subStringSize = wordSize * words.length;
for(int i = 0; i < n - subStringSize + 1; i++){
Map<String, Integer> temp = new HashMap<>();
int j = i;
for( ; j < i + subStringSize; j += wordSize){
String curWord = s.substring(j, j+ wordSize);
if(!map.containsKey(curWord)){
break;
}
temp.put(curWord, temp.getOrDefault(curWord,0)+1);
if(temp.get(curWord) > map.get(curWord)){
break;
}
}
if (j == i + subStringSize)
res.add(i);
}
return res;
}
}
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
counter = defaultdict(int)
res = []
single_length = len(words[0])
total_count = len(words)
total_words_length = total_count * single_length
for word in words:
counter[word] += 1
i = 0
while i + total_words_length <= len(s):
temp = defaultdict(int)
valid_count = 0
for j in range(i + single_length, i + total_words_length + 1, single_length):
temp_word = s[j - single_length:j]
if temp_word not in counter:
break
temp[temp_word] += 1
if temp[temp_word] > counter[temp_word]:
break
valid_count += 1
if valid_count == total_count:
res.append(i)
i += 1
return res
先把words当作一个word(需要words拼接后的size)在s中遍历,总共有s.size() - words.size() * words[0].size() + 1种可能,每次可得到一个substring。
两个hashmap,一个叫map存words中元素的次数,一个叫counts存上面循环实际得到的word的出现次数,进行对比。
由于words里面word的size一致,所以需要进行words.size()次循环,每次取一个word。
如果map中没有这个word,说明不对,break后进行下一次外循环,记得清空counts;
如果map中有这个word,把这个word放到counts里面,放入之后对比次数,如果counts里面这个word的次数大于这个word在map中的次数,也是不对的,break然后进行下一次外循环。
内层循环正常结束(说明内层循环进行了words.size()次),就把当前外循环的index值放入结果。
// Complexity. n = s.size(), m = words.size(), k = words[0].size();
// Time: O(n * m). Outer loop runs at most n times, acutally n - m*k times, inner loop runs exactly m times.
// Space: O(n), max size of two hashmaps is s.size() + words.size(), for example s = "abcd", words = ["a","b","c","d"],
// then both hashmaps should be {<"a", 1>, <"b", 1>, <"c", 1>, <"d", 1>}.
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
std::vector<int> result;
if(s.size() == 0 || words.size() == 0) return result;
int str_len = s.size();
int word_num = words.size();
int word_len = words[0].size();
int concate_len = word_num * word_len;
std::unordered_map<std::string, int> map;
std::unordered_map<std::string, int> counts;
for (auto word: words) {
map[word]++;
}
for (int i = 0; i < str_len - concate_len + 1; i++) {
counts.clear();
int j;
for (j = 0; j < word_num; j++) {
std::string temp = s.substr(i + j * word_len, word_len);
if (map.count(temp) == 0) break;
counts[temp]++;
if (counts[temp] > map[temp]) break;
}
if (j == word_num) result.push_back(i);
}
return result;
}
};
graph LR
A[Sliding Window] -->|滚动|B(HashMap)
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
// Target substring length
int targetL = words.length * words[0].length();
// Map: Key is words[] string, Value is corresponding freqency
Map<String, Integer> map = new HashMap<>();
for (String i : words) {
map.put(i, map.getOrDefault(i, 0) + 1);
}
// Sliding Window Begin
for (int i = 0; i <= s.length() - targetL; i++) {
String cur = s.substring(i, i + targetL);
if (isValid(cur, map, words[0].length())) { res.add(i);}
}
return res;
}
public boolean isValid(String cur, Map<String, Integer> map, int wordLength) {
// tmpMap: Key is string, Value is freqency
Map<String, Integer> tmpMap = new HashMap<>();
// Traversal by the length of word
for (int i = 0; i <= cur.length() - wordLength; i+=wordLength) {
String curS = cur.substring(i, i + wordLength);
// Does not contain
if (!map.containsKey(curS)) {
return false;
}
// Contain, but freq not right
tmpMap.put(curS, tmpMap.getOrDefault(curS, 0) + 1);
if (tmpMap.get(curS) > map.get(curS)) {
return false;
}
}
return true;
}
}
// T: O(s.len * words[0].len)
// S: O(words.len * words[0].len)
function findSubstring(s: string, words: string[]): number[] {
return findMatchedIndexes(s, words);
};
const findMatchedIndexes = (s: string, words: string[]) => {
const wordLen = words[0].length;
const tot = words.length * wordLen;
const wordCounts = countWords(words);
const matchedIndexes = [];
for (let i = 0; i + tot <= s.length; i++) {
const substringHash = splitSubstringToHash(s.substr(i, tot), wordLen);
if (isHashMatch(substringHash, wordCounts)) {
matchedIndexes.push(i);
}
}
return matchedIndexes;
};
const splitSubstringToHash = (substring: string, wordLen: number) => {
const sHash = {};
for (let j = 0; j < substring.length; j=j+wordLen) {
let substrWord = substring.substr(j, wordLen);
if (sHash[substrWord]) {
sHash[substrWord]++;
} else {
sHash[substrWord] = 1;
}
}
return sHash;
}
const isHashMatch = (hashA: object, hashB: object): boolean => {
const Akeys = Object.keys(hashA);
const Bkeys = Object.keys(hashB);
if (Akeys.length !== Bkeys.length) return false
let isMatching = true;
Akeys.find((aKey: string) => {
if (!hashB[aKey] || hashB[aKey] !== hashA[aKey]) {
isMatching = false;
return true;
}
})
Bkeys.find((bKey: string) => {
if (!hashA[bKey] || hashB[bKey] !== hashA[bKey]) {
isMatching = false;
return true;
}
})
return isMatching;
};
const countWords = (words: string[]) => {
const wordsHash = {};
words.forEach((word: string) => {
if (wordsHash[word]) {
wordsHash[word]++;
} else {
wordsHash[word] = 1;
}
})
return wordsHash;
};
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
window = len(words[0]) * len(words)
target = Counter(words)
i = 0
res = []
while i < len(s) - window + 1:
cut = []
start = i
for _ in range(len(words)):
cut.append(s[start:start + len(words[0])])
start += len(words[0])
if Counter(cut) == target:
res.append(i)
i += 1
return res
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> ans = new ArrayList<>();
int n = words.length;
int wordLen = words[0].length();
int subStrLen = n * wordLen;
if (s.length() < subStrLen) {
return ans;
}
Map<String, Integer> map = new HashMap<>();
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
for (int i = 0; i < wordLen; i++) {
Map<String, Integer> wordCount = new HashMap<>();
int count = 0;
int p = i;
for (int q = p; q + wordLen <= s.length(); q += wordLen) {
String key = s.substring(q, q + wordLen);
int limit = map.getOrDefault(key, 0);
if (limit == 0) {
wordCount.clear();
count = 0;
p = q + wordLen;
continue;
}
count++;
int num = wordCount.getOrDefault(key, 0) + 1;
wordCount.put(key, num);
while (num > limit) {
String str = s.substring(p, p + wordLen);
wordCount.put(str, wordCount.get(str) - 1);
count--;
p += wordLen;
if (str.equals(key)) {
num--;
}
}
if (count == words.length) {
ans.add(p);
}
}
}
return ans;
}
}
class Solution:
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
- 时间复杂度: O(m*n)
- 空间复杂度:O(N)
Sliding window
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
HashMap<String, Integer> strMap = new HashMap<>();
for(String word: words) {
strMap.put(word, strMap.getOrDefault(word, 0) + 1);
}
List<Integer> res = new ArrayList<>();
int i = 0;
int len = words[0].length();
while(i < s.length() - len * words.length + 1) {
int j = i;
int size = 0;
HashMap<String, Integer> map = new HashMap<>();
while(j + len <= s.length()) {
String word = s.substring(j, j + len);
j += len;
if(strMap.containsKey(word)) {
int count = map.getOrDefault(word, 0);
int wordCount = strMap.get(word);
if(count < wordCount) {
map.put(word, ++count);
if(count == wordCount) {
++size;
}
if(size == strMap.size()) {
res.add(i);
break;
}
continue;
}
}
break;
}
i++;
}
return res;
}
}
//optimized
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
HashMap<String, Integer> strMap = new HashMap<>();
for(String word: words) {
strMap.put(word, strMap.getOrDefault(word, 0) + 1);
}
List<Integer> res = new ArrayList<>();
int wordLen = words[0].length();
int size = words.length;
for (int i = 0; i < wordLen; i++) {
int left = i, right = i, count = 0;
HashMap<String, Integer> window = new HashMap<>();
while (right + wordLen <= s.length()) {
String r = s.substring(right, right + wordLen);
right += wordLen;
if (!strMap.containsKey(r)) {
count = 0;
left = right;
window.clear();
} else {
window.put(r, window.getOrDefault(r, 0) + 1);
count++;
while (window.getOrDefault(r, 0) > strMap.getOrDefault(r, 0)) {
String l = s.substring(left, left + wordLen);
count--;
window.put(l, window.getOrDefault(l, 0) - 1);
left += wordLen;
}
if (count == size) {
res.add(left);
}
}
}
}
return res;
}
}
Complexity Analysis
HashMap<String, Integer> count_words = new HashMap<>();
for(String word:words){
if(count_words.containsKey(word)){
count_words.replace(word, count_words.get(word)+1);
}else{
count_words.put(word,1);
}
}
int word_unit = words[0].length();
int string_length = word_unit * words.length;
ArrayList<Integer> result = new ArrayList<>();
for(int i = 0; i<s.length()-string_length+1;i++){
HashMap<String, Integer> count = new HashMap<>();
for(int j = i; j<i+string_length;j+=word_unit){
String current_word = s.substring(j,j+word_unit);
if(!count_words.containsKey(current_word)){
break;
}else{
if(count.containsKey(current_word)){
count.replace(current_word, count.get(current_word)+1);
if(count.get(current_word) > count_words.get(current_word)){
break;
}
}else{
count.put(current_word,1);
}
if((j-i)==(string_length-word_unit)){
result.add(i);
}
}
}
}
return result;
}
Complexity Analysis
字符串哈希
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
int n = s.size(), m = words.size(), w = words[0].size();
unordered_map<string, int> tot;
for (auto& word: words) tot[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] < tot[word]) cnt --;
}
auto word = s.substr(j, w);
wd[word] ++;
if (wd[word] <= tot[word]) cnt ++;
if (cnt == m) res.push_back(j - (m - 1) * w);
}
}
return res;
}
};
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
word_count = collections.Counter(words)
single = len(words[0])
all_length = len(words)
end = single * all_length - 1
start = 0
ans = []
while end < len(s):
current = s[start : end + 1]
in_window = []
for i in range(0, len(current), single):
word = current[i : i + single]
in_window.append(word)
if word_count == collections.Counter(in_window):
ans.append(start)
start += 1
end += 1
return ans
O(n)
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not words or not s:
return []
all_words = collections.Counter(words)
word_num = len(words)
word_length = len(words[0])
total_length = word_num * word_length
result = []
for i in range(len(s) - total_length + 1):
has_words = collections.defaultdict(int)
num = 0
while num < word_num:
current_word = s[i + num * word_length : i + (num+1) * word_length]
if current_word in all_words:
has_words[current_word] += 1
if has_words[current_word] > all_words[current_word]:
break
else:
break
num += 1
if num == word_num:
result.append(i)
return result
时间复杂度:O(M*N) 空间复杂度:O(N)
Language: Java
public List<Integer> findSubstring(String s, String[] words) {
if (s == null || words == null || s.length() == 0 || words.length == 0) {
return new ArrayList<>();
}
Map<String, Integer> counts = new HashMap<>();
for (String word : words) {
counts.put(word, counts.getOrDefault(word, 0) + 1);
}
List<Integer> r = new ArrayList<>();
int sLen = s.length();
int num = words.length;
int wordLen = words[0].length();
for (int i = 0; i < sLen - num * wordLen + 1; i++) {
String sub = s.substring(i, i + num * wordLen);
if (isConcat(sub, counts, wordLen)) {
r.add(i);
}
}
return r;
}
private boolean isConcat(String sub, Map<String, Integer> counts, int wordLen) {
Map<String, Integer> seen = new HashMap<>();
for (int i = 0; i < sub.length(); i += wordLen) {
String sWord = sub.substring(i, i + wordLen);
seen.put(sWord, seen.getOrDefault(sWord, 0) + 1);
}
return seen.equals(counts);
}
哈希表数组,将string看做整体,分割匹配
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
//小写字母,数组哈希,+滑动窗口,
vector<int>res;
int m=words.size(),n=words[0].size(),slen=s.size();
for(int i=0;i<n && i+m*n<=slen;i++){
unordered_map<string,int>differ;
for(int j=0;j<m;j++){
++differ[s.substr(i+j*n,n)];
}
for(string word:words){
if(--differ[word]==0)differ.erase(word);
}
for(int start=i;start<slen-m*n+1;start+=n){
if(start!=i){
string word=s.substr(start+(m-1)*n,n);
if(++differ[word]==0)differ.erase(word);
word=s.substr(start-n,n);
if(--differ[word]==0)differ.erase(word);
}
if(differ.empty())res.push_back(start);
}
}
return res;
}
};
var findSubstring = function(s, words) {
const res = [];
const m = words.length, n = words[0].length, ls = s.length;
for (let i = 0; i < n; i++) {
if (i + m * n > ls) {
break;
}
const differ = new Map();
for (let j = 0; j < m; j++) {
const word = s.substring(i + j * n, i + (j + 1) * n);
differ.set(word, (differ.get(word) || 0) + 1);
}
for (const word of words) {
differ.set(word, (differ.get(word) || 0) - 1);
if (differ.get(word) === 0) {
differ.delete(word);
}
}
for (let start = i; start < ls - m * n + 1; start += n) {
if (start !== i) {
let word = s.substring(start + (m - 1) * n, start + m * n);
differ.set(word, (differ.get(word) || 0) + 1);
if (differ.get(word) === 0) {
differ.delete(word);
}
word = s.substring(start - n, start);
differ.set(word, (differ.get(word) || 0) - 1);
if (differ.get(word) === 0) {
differ.delete(word);
}
}
if (differ.size === 0) {
res.push(start);
}
}
}
return res;
};
滑动窗口 + 哈希表
class Solution {
public:
vector<int> findSubstring(string &s, vector<string> &words) {
vector<int> res;
int m = words.size(), n = words[0].size(), ls = s.size();
// 用单词长度n来分割string s,余数小于n,分别将这些余数作为左指针
for (int i = 0; i < n && i + m * n <= ls; ++i) {
//一个窗口中包含s中的前m个单词,使用一个哈希表来记录窗口中的单词频次和word中的单词频次
unordered_map<string, int> differ;
//初始化,某word在窗口中出现对应值加1,在words中出现对应值减1
//初始化结束后,若某单词在哈希表中值为0,则word在窗口和words中只出现一次
for (int j = 0; j < m; ++j) {
++differ[s.substr(i + j * n, n)];
}
for (string &word: words) {
if (--differ[word] == 0) {
differ.erase(word);
}
}
//窗口移动,右侧增加一个单词,左侧减少一个单词
for (int start = i; start < ls - m * n + 1; start += n) {
if (start != i) {
string word = s.substr(start + (m - 1) * n, n);
if (++differ[word] == 0) {
differ.erase(word);
}
word = s.substr(start - n, n);
if (--differ[word] == 0) {
differ.erase(word);
}
}
//哈希表为空,说明窗口单词出现频次与words吻合
if (differ.empty()) {
res.emplace_back(start);
}
}
}
return res;
}
};
var findSubstring = function(s, words) {
let res = []
let map = {}
words.forEach(e => {
if(map[e]) map[e] = map[e] + 1
else map[e] = 1
})
let charLength = words[0].length
let strLength = charLength * words.length
let l = 0, r = strLength - 1;
while(r < s.length) {
let [left,right] = [l,r]
let tmpMap = {...map}
let flag = true
while(left <= right) {
let strLeft = s.slice(left, left + charLength)
if(tmpMap[strLeft] > 0) {
tmpMap[strLeft] --
left += charLength
} else {
flag = false
break;
}
}
if(flag) {
res.push(l)
}
l += 1
r += 1
}
return res
};
class Solution(object):
def findSubstring(self, s, words):
if len(words) == 0:
return []
l = len(words[0])
d = {}
for w in words:
if w not in d:
d[w] = 1
else:
d[w] += 1
i = 0
ans = []
for k in range(1):
left = k
subd = {}
count = 0
for j in range(k,len(s)-l+1,l):
tword = s[j:j+l]
if tword in d:
if tword in subd:
subd[tword] += 1
else:
subd[tword] = 1
count += 1
while subd[tword] > d[tword]:
subd[s[left:left+l]] -= 1
count -= 1
left += l
if count == len(words):
ans.append(left)
else:
subd = {}
count = 0
left = j + l
return ans
/****
Solution:
l iterate through whole s.size()
we can divide this iteration into word_len parts
0: l = 0 + word_len * 0, 0 + word_len * 1, 0 + word_len * 2....
1: l = 1 + word_len * 0, 1 + word_len * 1, 1 + word_len * 2....
...
word_len -1 : l = word_len-1, word_len - 1 + word_len * 1
Then we can jump! make use of the sliding window to avoid duplication calculation
since len of target substring is fixed = words.size()
we can track the words we have found, if found == words.size() && no negative used, then
we found an answer
Sliding window:
r = l
cur = s[r, word_len]
for a new string:
1. if cur in seen_map,
--seen_map[cur], ++wordused
while seen_map[cur] < 0, move left
if seen_map[cur] == 0, if wordused == targetword,
found ! move left to left+word_len, ++seen_map[leftword], --wordused
2. if cur not in seen_map, l = r + word_len, reset
DONT COPY MAP!
s.size() = n
words.size() = a
word length = b
construct seen map: Time a, Space a
loop: Time b * sliding_window
Sliding_window: left and right pointer, n
Time: O(a + n * b)
Space:O(a)
***/
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int target_len = words.size();
int word_len = words[0].size();
vector<int> results;
unordered_map<string, int> seen;
for (auto word : words) {
if (seen.find(word) != seen.end()) {
seen[word] += 1;
}
else {
seen[word] = 1;
}
}
for (int l = 0; l < word_len; ++l) {
sliding_window(s, seen, target_len, word_len, results, l);
}
return results;
}
void sliding_window(const string& s,
unordered_map<string, int> &count,
int target_len,
int word_len,
vector<int> &results,
int l) {
unordered_map<string, int> seen;
int word_used = 0;
for (int r = l; r < s.size(); r += word_len) {
string cur = s.substr(r, word_len);
// cout << "l: " << l << "r :" << r << " cur:" << cur << endl;
if (count.find(cur) != count.end()) {
if (seen.find(cur) != seen.end()) {
seen[cur] += 1;
}
else {
seen[cur] = 1;
}
++word_used;
while (seen[cur] > count[cur]) {
string l_word = s.substr(l, word_len);
seen[l_word] -= 1;
l += word_len;
--word_used;
}
if (word_used == target_len) {
results.push_back(l);
string l_word = s.substr(l, word_len);
seen[l_word] -= 1;
l += word_len;
--word_used;
// cout << "l " << l << "r" << r << endl;
}
while (word_used > target_len) {
string l_word = s.substr(l, word_len);
seen[l_word] -= 1;
l += word_len;
--word_used;
}
// cout << "dddd";
// r = max (r, l);
}
else {
l = r + word_len;
r = l - word_len;
seen.clear();
word_used = 0;
}
}
}
};
func findSubstring(s string, words []string) []int {
wordFrequency := make(map[string]int)
for _, word := range words {
wordFrequency[word]++
}
var res []int
length := len(words[0])
for i := 0; i < len(s)-length*len(words)+1; i++ {
seen := make(map[string]int)
for j := 0; j < len(words); j++ {
nextIndex := i + j*length
word := s[nextIndex : nextIndex+length]
if _, ok := wordFrequency[word]; !ok {
break
}
seen[word]++
seenFrequency, _ := seen[word]
originFrequency, _ := wordFrequency[word]
if seenFrequency > originFrequency {
break
}
if j+1 == len(words) {
res = append(res, i)
}
}
}
return res
}
滑动窗口 用一个map保存源数组中的单词频率 临时map统计滑动窗口中的 单词频率,然后两个map作比较
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
int len = 0;
Map<String, Integer> map = new HashMap<>();
// 统计单词频率 总长度
for (int i = 0; i < words.length; i++) {
len += words[i].length();
map.put(words[i], map.getOrDefault(words[i], 0) + 1);
}
// 单个字符串长度
int wLen = words[0].length();
List<Integer> list = new ArrayList<>();
for (int i = 0; i <= s.length() - len; i++) {
// 总长度len的串 从位置0开始向右滑动
String sub = s.substring(i, i + len);
Map<String, Integer> curMap = new HashMap<>();
boolean f = true;
for (int j = 0; j < sub.length(); j += wLen) {
// 记录当前滑动的串 单词出现的频率 放在临时map中
String r = sub.substring(j, j + wLen);
curMap.put(r, curMap.getOrDefault(r, 0) + 1);
}
// 比较临时map中的单词频率 和原始map中的对比 不符合就直接淘汰
Set<String> set1 = map.keySet();
for (String key : set1) {
Integer cv = curMap.get(key);
Integer v = map.get(key);
if (cv == null || (cv - v != 0)) {
f = false;
break;
}
}
// 符合就就加入
if (f) list.add(i);
}
return list;
}
}
滑动窗口+哈希表
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
HashMap<String, Integer> count = new HashMap<>();
for (String word : words) {
count.put(word, count.getOrDefault(word, 0) + 1);
}
List<Integer> index = new ArrayList<>();
int n = s.length(), wordsCount = words.length, wordsLength = words[0].length();
for (int i = 0; i <= n - wordsCount * wordsLength; i++) {
HashMap<String, Integer> wordsSeen = new HashMap<>();
for (int j = 0; j < wordsCount; j++) {
int nextWordIndex = i + j * wordsLength;
String word = s.substring(nextWordIndex, nextWordIndex + wordsLength);
if (!count.containsKey(word)) {
break;
}
wordsSeen.put(word, wordsSeen.getOrDefault(word, 0) + 1);
if (wordsSeen.get(word) > count.getOrDefault(word, 0)) {
break;
}
if (j + 1 == wordsCount) {
index.add(i);
}
}
}
return index;
}
}
Substring with Concatenation of All Words - LeetCode
Substring problem, use sliding window.
For each position i
, can search for the condition. If meet, record. Else, find next one.
How to search?
Sliding window with hashMap.
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
/*
Time Complexity: O(N^2);
Space Complexity: O(N);
*/
// Store all the words in hashMap
Map<String, Integer> need = new HashMap<>();
int valid = 0;
for (String w : words) {
if (!need.containsKey(w)) {
valid++;
}
need.put(w, need.getOrDefault(w, 0) + 1);
}
int k = words[0].length();
int n = s.length();
int m = words.length;
List<Integer> res = new ArrayList<>();
//System.out.printf("New test, valid=%d, n=%d, m*k=%d \n",valid, n, m*k);
for (int l = 0; l <= n - k * m; l++) {
// for every possible position, perform sliding window
int cnt = 0; // valid matches
Map<String, Integer> window = new HashMap<>(); // HashMap, store valus in window.
int r = l;
while (r + k <= n) {
String cur = s.substring(r, r+k);
if (!need.containsKey(cur)) break; // unknown, break;
window.put(cur, window.getOrDefault(cur, 0) + 1); // plus 1
if (window.get(cur).equals(need.get(cur))) cnt++; // find a valid one
if (window.get(cur) > (need.get(cur))) break; // more than need, break;
//System.out.printf("l=%d, r=%d,cnt=%d\n",l,r,cnt);
if (cnt == valid) {
res.add(l);
break;
}
r+=k;
}
}
return res;
}
}
Time Complexity
O(N^2)
Space Complexity
O(N)
CODE
执行用时:84 ms, 在所有 Python3 提交中击败了90.95%的用户 内存消耗:15.5 MB, 在所有 Python3 提交中击败了70.96%的用户 //滑动窗口还不是很理解,二刷自己尝试实现----
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
res = []
m,n,ls = len(words),len(words[0]),len(s)
for i in range(n):
if i+m*n > ls:
break
differ = Counter()
for j in range(m):
word = s[i+j*n:i+(j+1)*n]
differ[word] += 1
for word in words:
differ[word] -= 1
if differ[word] == 0:
del differ[word]
for start in range(i,ls-m*n+1,n):
if start != i:
word = s[start+(m-1)*n:start+m*n]
differ[word] += 1
if differ[word] == 0:
del differ[word]
word = s[start-n:start]
differ[word] -= 1
if differ[word] == 0:
del differ[word]
if len(differ) == 0:
res.append(start)
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 []
# 哈希表统计单词出现次数,用以后续比较
word_cnt = Counter(words)
# 单词长度
word_len = len(words[0])
# 返回结果列表
ans = []
# 遍历,进行窗口滑动
for i in range(word_len):
left = i
right = i
cnt = 0
# 哈希表记录窗口的单词出现次数
window = Counter()
# 限定边界
# 这里表示窗口的内容不足以组成串联所有单词的子串,循环结束
while left + len(words) * word_len <= len(s):
# 窗口单词出现的次数,与 word_cnt 对比
while cnt < len(words):
word = s[right:right+word_len]
# 如果单词不在 words 中,或者此时单词数量大于 words 中的单词数量时,退出循环另外处理
# 单词次数相等也跳出另外判断
# 否则更新哈希表 window
if (word not in words) or (window[word] >= word_cnt[word]):
break
window[word] += 1
cnt += 1
right += word_len
# 先判断哈希表是否相等,相等则加入返回列表中
if word_cnt == window:
ans.append(left)
# 再处理单词数溢出的情况
# 区分在于单词是否在 words 中
if word in words:
# 剔除左边部分
left_word = s[left: left+word_len]
window[left_word] -= 1
left += word_len
cnt -= 1
else:
# 如果单词不在 words 中,
# 清空哈希表,重置窗口开始位置
right += word_len
window.clear()
left = right
cnt = 0
return ans
- 时间复杂度: O(N**2)
- 空间复杂度: O(N)
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
res = []
n = len(words[0])
if n>len(s):
return res
wordstore = defaultdict(int)
for item in words:
wordstore[item] += 1
first = [item[0] for item in words]
i,j = 0,0
while j < len(s):
while j < len(s) and s[j] not in first:
j+=1
i=j
cur = defaultdict(int)
while s[i:i+n] in wordstore:
cur[s[i:i+n]] += 1
if cur[s[i:i+n]] > wordstore[s[i:i+n]]:
break
i = i+n
if wordstore == cur:
res.append(j)
break
j+=1
return res
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
using std::vector;
using std::string;
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if (s.size() == 0 || words.size() == 0 || s.size() < words.size()) {
return std::vector<int>{};
}
std::unordered_map<std::string, int> dict1;
int min_word_size = words[0].size();
int max_word_size = 0;;
for (const std::string & word : words) {
++dict1[word];
min_word_size = min_word_size < word.size() ? min_word_size: word.size();
max_word_size = max_word_size > word.size() ? max_word_size: word.size();
}
// 将s按照dict1的写法进行分词
// 分词的话,可能出现包含关系?如果是,保险起见应该按最大字符串原则进行分词
std::vector<std::vector<int>> seg_index;
for (int i = 0; i < s.size(); ++i) {
for (int j = i + max_word_size; j >= i + min_word_size; --j) {
if (dict1.count(s.substr(i, j - i)) > 0) {
seg_index.push_back({i, j});
}
}
}
if (seg_index.size() < words.size()) {
return std::vector<int> {};
}
// 如果有包含关系,还需要双重for循环来去除,这里先暂时不考虑
// 下一步,利用长短指针,来找到是否有包含片段,确保两个dict完全一致
int i = 0;
std::unordered_map<std::string, int> temp_dict;
std::vector<int> res;
int last_index = 0;
int last_count = 0;
for (int i = 0; i < seg_index.size(); ++i){
if (dict1.count(s.substr(seg_index[i][0], seg_index[i][1] - seg_index[i][0])) == 0) {
continue;
}
last_index = seg_index[i][1];
temp_dict.clear();
temp_dict[s.substr(seg_index[i][0], seg_index[i][1] - seg_index[i][0])] = 1;
last_count = 1;
for (int j = i + 1; j < seg_index.size(); ++j) {
if (last_count >= words.size()) {
break;
}
if (seg_index[j][0] == last_index) {
++temp_dict[s.substr(seg_index[j][0], seg_index[j][1] - seg_index[j][0])];
++last_count;
last_index = seg_index[j][1];
}
}
// 判断temp_dict与dict1是否完全一致
if (temp_dict.size() == dict1.size() && last_count == words.size()) {
bool equal = true;
for(const auto x: dict1) {
if (temp_dict[x.first] != x.second) {
equal = false;
}
}
if (equal) {
res.push_back(seg_index[i][0]);
}
}
}
return std::move(res);
}
};
int main() {
std::string s = "barfoofoobarthefoobarman";
std::vector<std::string> words({"bar","foo","the"});
Solution so;
std::vector<int> res = so.findSubstring(s, words);
for (int i = 0; i < res.size(); ++i) {
std::cout << res[i] << ",";
}
std::cout << std::endl;
}
class Solution {
public:
vector<int> findSubstring(const string & s, vector<string>& words) {
int word_size = words[0].size();
int word_num = words.size();
if (s.size() == 0 || words.size() == 0 || s.size() < word_size * word_num) {
return std::vector<int>{};
}
std::unordered_map<std::string, int> dict1;
for (const std::string & word : words) {
++dict1[word];
}
std::unordered_map<std::string, int> temp_dict;
std::vector<int> res;
int last_index = 0;
int last_count = 0;
int n = s.size() - word_num * word_size;
for (int i = 0; i <= n; ++i) {
temp_dict.clear();
for (int j = i; j < i + word_size * word_num; j += word_size) {
// 加一个提前退出
if (dict1.count(s.substr(j, word_size)) == 0) {
break;
}
++temp_dict[s.substr(j, word_size)];
}
// 判断temp_dict与dict1是否一致
if (temp_dict.size() == dict1.size()) {
bool compare_res = true;
for (const auto x: dict1) {
if (temp_dict[x.first] != x.second) {
compare_res = false;
break;
}
}
if (compare_res) {
res.push_back(i);
}
}
}
return std::move(res);
}
};
/*
* Algorithm: Sliding Window + HashMap
* Time Complexity: O((n-k)*k)
* Space Complexity: O(m)
*/
function findSubstring(s: string, words: string[]): number[] {
const ret = []
const wordSize = words[0].length, subStrLen = words.length * wordSize
const hash = new Map<string, number>()
words.forEach((w) => hash.set(w, (hash.get(w) || 0) + 1))
for (let i = 0; i <= s.length - subStrLen; i++) {
const tmpHash = new Map(hash)
let wordCount = words.length
for (let j = i; j < i + subStrLen; j += wordSize) {
const word = s.slice(j, j + wordSize)
const count = tmpHash.get(word)
if (!count) break
if (--wordCount === 0) {
ret.push(i)
break
}
tmpHash.set(word, count - 1)
}
}
return ret
};
滑动窗口扫描字符串,hash表统计单词出现次数。
class Solution:
def word_cnt_equals(self,
word_cnt: Dict[str, int],
sub_word_cnt: Dict[str, int]) -> bool:
if len(word_cnt) != len(sub_word_cnt):
return False
for word, cnt in word_cnt.items():
if cnt != sub_word_cnt[word]:
return False
return True
def findSubstring(self, s: str, words: List[str]) -> List[int]:
str_len = len(s)
word_len = len(words[0])
substr_len = word_len * len(words)
if str_len < substr_len:
return []
word_cnt = Counter(words)
ret = []
for l in range(str_len - substr_len + 1):
r = l + substr_len - 1
sub_words = []
for i in range(l, r + 1, word_len):
sub_words.append(s[i:i + word_len])
sub_word_cnt = Counter(sub_words)
if self.word_cnt_equals(word_cnt, sub_word_cnt):
ret.append(l)
return ret
n = s.length, m = words.length, k = words[0].length
先统计words数组中每个单词出现的次数,然后再将s字符串进行切分,每次切出来的长度为len(words[0])*len(word)的长度,比较这个字串中的单词是否与words中单词出现的次数一样即楞。 若不满足,则将s向前滑动一个位置,切分出一个新的子串,如此循环。
func findSubstring(s string, words []string) []int {
wordNum := len(words)
wordLen := len(words[0])
allWorkLen := wordNum * wordLen
wordsCount := make(map[string]int, len(words))
for _, word := range words {
if _, ok := wordsCount[word]; ok {
wordsCount[word] += 1
} else {
wordsCount[word] = 1
}
}
var match func(ss string) bool
match = func(ss string) bool {
num := 0
hasCount := make(map[string]int, len(words))
for i := 0; i < len(ss); i += wordLen {
word := ss[i:i+wordLen]
if _, ok := wordsCount[word]; !ok {
return false
}
num++
if _, ok := hasCount[word]; !ok {
hasCount[word] = 1
continue
}
hasCount[word]++
if hasCount[word] > wordsCount[word] {
return false
}
}
return num == wordNum
}
ret := make([]int, 0)
for i := 0; i <= len(s) - allWorkLen; i++ {
if match(s[i:i+allWorkLen]) {
ret = append(ret, i)
}
}
return ret
}
思路
1.明确描述,words里面词长度一样,参考官方题解,对每一个s的index位置做一个l*n的滑窗并进行分割,看是否与words的hashmap一致,如果一致就记录下当前的位置
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
ls, l, n = len(s), len(words), len(words[0])
counter = collections.Counter(words)
ans = []
for i in range(ls):
temp = []
if i+l*n>ls:
break
for j in range(i,i+l*n,n):
temp.append(s[j:j+n])
if collections.Counter(temp)==counter:
ans.append(i)
return ans
复杂度
1.时间复杂度O(mn)
2.空间复杂度O(n)
words
中各个单词的个数,从s
的每个字母开始依次往后顺序取单词(s[i,i+word_len]
)进行遍历
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:return []
from collections import Counter
dict1 = Counter(words)
n_words = len(words)
word_len = len(words[0])
res = []
for i in range(len(s)-word_len*n_words+1):
hash_words = {}
num = 0
while num < n_words:
word = s[i+num*word_len:i+(num+1)*word_len]
if word in dict1:
word_time = hash_words.get(word,0)
hash_words[word] = word_time + 1
if hash_words.get(word) > dict1.get(word): # 某个单词个数不一致
break
else:
break
num+=1
if num == n_words:
res.append(i)
return res
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
int n = s.length(), m = words.length, w = words[0].length();
Map<String, Integer> map = new HashMap<>();
for (String word : words) map.put(word, map.getOrDefault(word, 0) + 1);
List<Integer> ans = new ArrayList<>();
out:for (int i = 0; i + m * w <= n; i++) {
Map<String, Integer> cur = new HashMap<>();
String sub = s.substring(i, i + m * w);
for (int j = 0; j < sub.length(); j += w) {
String item = sub.substring(j, j + w);
if (!map.containsKey(item)) continue out;
cur.put(item, cur.getOrDefault(item, 0) + 1);
}
if (cur.equals(map)) ans.add(i);
}
return ans;
}
}
var findSubstring = function(s, words) { const res = []; const m = words.length, n = words[0].length, ls = s.length; for (let i = 0; i < n; i++) { if (i + m n > ls) { break; } const differ = new Map(); for (let j = 0; j < m; j++) { const word = s.substring(i + j n, i + (j + 1) n); differ.set(word, (differ.get(word) || 0) + 1); } for (const word of words) { differ.set(word, (differ.get(word) || 0) - 1); if (differ.get(word) === 0) { differ.delete(word); } } for (let start = i; start < ls - m n + 1; start += n) { if (start !== i) { let word = s.substring(start + (m - 1) n, start + m n); differ.set(word, (differ.get(word) || 0) + 1); if (differ.get(word) === 0) { differ.delete(word); } word = s.substring(start - n, start); differ.set(word, (differ.get(word) || 0) - 1); if (differ.get(word) === 0) { differ.delete(word); } } if (differ.size === 0) { res.push(start); } } } return res; };
sliding window:
code
class Solution {
private HashMap<String, Integer> wordCount = new HashMap<String, Integer>();
private int n;
private int wordLength;
private int substringSize;
private int k;
private void slidingWindow(int left, String s, List<Integer> answer) {
HashMap<String, Integer> wordsFound = new HashMap<>();
int wordsUsed = 0;
boolean excessWord = false;
// Do the same iteration pattern as the previous approach - iterate
// word_length at a time, and at each iteration we focus on one word
for (int right = left; right <= n - wordLength; right += wordLength) {
String sub = s.substring(right, right + wordLength);
if (!wordCount.containsKey(sub)) {
// Mismatched word - reset the window
wordsFound.clear();
wordsUsed = 0;
excessWord = false;
left = right + wordLength;
} else {
// If we reached max window size or have an excess word
while (right - left == substringSize || excessWord) {
String leftmostWord = s.substring(left, left + wordLength);
left += wordLength;
wordsFound.put(leftmostWord, wordsFound.get(leftmostWord) - 1);
if (wordsFound.get(leftmostWord) >= wordCount.get(leftmostWord)) {
// This word was an excess word
excessWord = false;
} else {
// Otherwise we actually needed it
wordsUsed--;
}
}
// Keep track of how many times this word occurs in the window
wordsFound.put(sub, wordsFound.getOrDefault(sub, 0) + 1);
if (wordsFound.get(sub) <= wordCount.get(sub)) {
wordsUsed++;
} else {
// Found too many instances already
excessWord = true;
}
if (wordsUsed == k && !excessWord) {
// Found a valid substring
answer.add(left);
}
}
}
}
public List<Integer> findSubstring(String s, String[] words) {
n = s.length();
k = words.length;
wordLength = words[0].length();
substringSize = wordLength * k;
for (String word : words) {
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
List<Integer> answer = new ArrayList<>();
for (int i = 0; i < wordLength; i++) {
slidingWindow(i, s, answer);
}
return answer;
}
}
slide window
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
int num = words.length;
int wordLen = words[0].length();
int stringLen = s.length();
for(int i = 0; i < wordLen; i++) {
if(i + num * wordLen > stringLen) {
//If the length exceeds the length of S, then the following cannot be correct
break;
}
Map<String, Integer> differ = new HashMap<>();//the differ between windows and words
for(int j = 0; j < num; j++) {
//Initialized window, length is num * wordLen, store each word
String word = s.substring(i + j * wordLen, i + (j + 1) * wordLen);
//shows each word
differ.put(word, differ.getOrDefault(word, 0) + 1);
}
for(String word : words) {
//Calculate the difference between words and window
differ.put(word, differ.getOrDefault(word, 0) - 1);
if(differ.get(word) == 0) {
differ.remove(word);
}
}
for(int start = i; start < stringLen - num * wordLen + 1; start += wordLen) {
if(start != i) {
//Firstly record the word on the right, and then delete the word on the left
//record the words on the right
String word = s.substring(start + (num - 1) * wordLen, start + num * wordLen);
differ.put(word, differ.getOrDefault(word, 0) + 1);
if(differ.get(word) == 0) {
differ.remove(word);
}
//delete the word on the left
word = s.substring(start - wordLen, start);
differ.put(word, differ.getOrDefault(word, 0) - 1);
if(differ.get(word) == 0) {
differ.remove(word);
}
}
if(differ.isEmpty()) {
res.add(start);
}
}
}
return res;
}
}
Complexity Analysis
想不出来,暴力解法
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
int wlen=words.length,len=words[0].length(),slen=s.length();
HashMap<String,Integer> map=new HashMap<>();
for(int i=0;i<wlen;i++){
map.put(words[i],map.getOrDefault(words[i],0)+1);
}
Map<String, Integer> clone = null;
Map<String,Integer> empty=new HashMap<>();
List<Integer> list=new ArrayList<>();
int n=slen-wlen*len+1;
String temp="";
for(int i=0;i<n;i++){
clone=(Map<String, Integer>) map.clone();
for(int j=i;j<i+wlen*len;j+=len){
temp=s.substring(j,j+len);
if(!clone.containsKey(temp))
break;
if(clone.get(temp)>1)
clone.put(temp,clone.get(temp)-1);
else
clone.remove(temp);
}
if(empty.equals(clone)){
list.add(i);
}
}
return list;
}
}
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
int single = words[0].length();
List<Integer> list = new ArrayList<>();
if(s.length() < single) return list;
Map<String, Integer> map = new HashMap<>();
for(String str : words)
map.put(str, map.getOrDefault(str, 0)+1);
int left = 0;
int right = 0;
for(int i = 0; i < single; i++){
left = i;
right= i;
Map<String, Integer> tsMap = new HashMap<>();
while(right + single <= s.length()){
String ts = s.substring(right, right+single);
int tCount = tsMap.getOrDefault(ts, 0);
int mCount = map.getOrDefault(ts, 0);
if(mCount == 0){
while(left != right){
tsMap.put(s.substring(left, left+single), tsMap.get(s.substring(left, left+single))-1);
left += single;
}
left += single;
}
else if(tCount < mCount) tsMap.put(ts, tCount+1);
else{
tsMap.put(ts, tCount+1);
while(tsMap.getOrDefault(ts, 0) > map.getOrDefault(ts, 0)){
String ls = s.substring(left, left+single);
tsMap.put(ls, tsMap.get(ls)-1);
left += single;
}
}
right += single;
if(right-left == single*words.length){
list.add(left);
tsMap.put(s.substring(left, left+single), tsMap.get(s.substring(left, left+single))-1);
left += single;
}
}
}
return list;
}
}
先用哈希表存储每个单词的数量;遍历一遍字符串,然后内部while循环每一段单词长度的字符串是否在哈希表中,若匹配到哈希表中的单词,该单词的熟练减一,直到循环到的字符串不在哈希表中或对应单词数量都为0;若最后内部循环结束,走过的字符串长度等于所有单词对长度和,则说明当前while循环到的字符串是所有单词的串联。
var findSubstring = function(s, words) {
const wordsNum = words.length
const len = words[0].length
let map = {}
let res = []
words.forEach(item => {
map[item] = (map[item] || 0) + 1
})
let i = 0
while (i < s.length - wordsNum * len + 1 ) { // 剪枝
let k = i
let word = s.slice(k, k + len)
while (map[word]) {
map[word]--
k += len
word = s.slice(k, k + len)
}
// 需要把map复原
map = {}
words.forEach(item => {
map[item] = (map[item] || 0) + 1
})
if (k === i + wordsNum * len) {
res.push(i)
}
i++
}
return res
};
时间:O(nm), 字符串遍历O(n), 内部循环单词数组O(m) 空间:O(n + m), 结果存储空间,最坏为存所有字符串的下标O(n), 哈希表O(m) (分析应该不是很对)
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
final Map<String, Integer> counts = new HashMap<>();
for (final String word : words) {
counts.put(word, counts.getOrDefault(word, 0) + 1);
}
final List<Integer> indexes = new ArrayList<>();
final int n = s.length(), num = words.length, len = words[0].length();
for (int i = 0; i < n - num * len + 1; i++) {
final Map<String, Integer> seen = new HashMap<>();
int j = 0;
while (j < num) {
final String word = s.substring(i + j * len, i + (j + 1) * len);
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 == num) {
indexes.add(i);
}
}
return indexes;
}
}
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
const res = [];
const m = words.length, n = words[0].length, ls = s.length;
for (let i = 0; i < n; i++) {
if (i + m * n > ls) {
break;
}
const differ = new Map();
for (let j = 0; j < m; j++) {
const word = s.substring(i + j * n, i + (j + 1) * n);
differ.set(word, (differ.get(word) || 0) + 1);
}
for (const word of words) {
differ.set(word, (differ.get(word) || 0) - 1);
if (differ.get(word) === 0) {
differ.delete(word);
}
}
for (let start = i; start < ls - m * n + 1; start += n) {
if (start !== i) {
let word = s.substring(start + (m - 1) * n, start + m * n);
differ.set(word, (differ.get(word) || 0) + 1);
if (differ.get(word) === 0) {
differ.delete(word);
}
word = s.substring(start - n, start);
differ.set(word, (differ.get(word) || 0) - 1);
if (differ.get(word) === 0) {
differ.delete(word);
}
}
if (differ.size === 0) {
res.push(start);
}
}
}
return res;
}
暴力破解,但是这两个方法超时
//
// findSubstring
// @Description:
// @param s
// @param words
// @return []int
//
func findSubstring(s string, words []string) []int {
ans := make([]int, 0)
wordLength := len(words[0])
window := wordLength * len(words)
if len(s) < window {
return []int{}
}
left := 0
right := window
for right <= len(s) {
// 排序
sub := make([]string, 0)
ss := string(s[left:right])
fmt.Println(ss)
for i := 0; i < len(ss)/wordLength; i++ {
sub = append(sub, ss[wordLength*i:wordLength*i+wordLength])
}
sort.Slice(sub, func(i, j int) bool {
return sub[i] > sub[j]
})
w := words
sort.Slice(w, func(i, j int) bool {
return w[i] > w[j]
})
tmp := true
fmt.Println(sub, w)
for i := 0; i < len(w); i++ {
if string(sub[i]) != string(w[i]) {
tmp = false
break
}
}
if tmp {
ans = append(ans, left)
}
left++
right++
}
return ans
}
//
// findSubstring
// @Description: 超时
// @param s
// @param words
// @return []int
//
func findSubstring1(s string, words []string) []int {
ans := make([]int, 0)
wordLength := len(words[0])
window := wordLength * len(words)
shashMap := make(map[string]int)
for _, ch := range words {
shashMap[ch]++
}
if len(s) < window {
return []int{}
}
left := 0
right := window
for right <= len(s) {
// 排序
sub := string(s[left:right])
newHashmap := make(map[string]int)
for i := 0; i < len(sub)/wordLength; i++ {
newHashmap[sub[wordLength*i:wordLength*i+wordLength]]++
}
tmp := true
for key, value := range shashMap {
if v, k := newHashmap[key]; k {
if v != value {
tmp = false
}
} else {
left++
right++
continue
}
}
if tmp {
ans = append(ans, left)
}
}
return ans
}
复杂度分析
双哈希表+滑动窗口
//
// findSubstring2
// @Description:
// @param s
// @param words
// @return []int
//
func findSubstring2(s string, words []string) []int {
wordLen := len(words[0])
wordsLen := len(words)
length := wordsLen * wordLen
wordsHash := map[string]int{}
for _, word := range words {
wordsHash[word]++
}
ans := make([]int, 0)
for i := 0; i+length <= len(s); i++ {
cnt := 0
subHash := map[string]int{}
sub := s[i : i+length]
for j := 0; j < wordsLen; j++ {
item := sub[j*wordLen : (j+1)*wordLen]
if _, ok := wordsHash[item]; !ok {
break
} else {
if subHash[item] == wordsHash[item] {
break
}
subHash[item]++
cnt++
}
}
if len(wordsHash) == len(subHash) && cnt == wordsLen {
ans = append(ans, i)
}
}
return ans
}
复杂度分析
var findSubstring = function(s, words) {
const wordSize = words[0].length;
const wordsLen = wordSize * words.length;
let map = new Map();
let ans = [];
for (let i = 0; i< words.length; i++) {
map.has(words[i]) ? map.set(words[i], map.get(words[i]) + 1) : map.set(words[i], 1);
}
for (let i = 0; i < s.length - wordsLen + 1; i++) {
const tmap = new Map(map);
let count = words.length;
for (let p = i; p < i + wordsLen; p += wordSize) {
const word = s.slice(p, p + wordSize);
if (!tmap.has(word) || tmap.get(word) <= 0) {
break;
}
tmap.set(word, tmap.get(word) - 1);
count--;
}
if (count === 0) {
ans.push(i);
}
}
return ans;
};
var findSubstring = function(s, words) {
const wordSize = words[0].length
const substringLen = wordSize * words.length
const wordsCount = {}
words.forEach(w => (wordsCount[w] = (wordsCount[w] || 0) + 1))
const res = []
for (let i = 0; i <= s.length - substringLen; i++) {
const tempCount = {...wordsCount}
let count = words.length
for (let j = i; j < i + substringLen; j += wordSize) {
const word = s.slice(j, j + wordSize)
if (!(word in tempCount) || tempCount[word] <= 0) break
tempCount[word]--
count--
}
if (count === 0) res.push(i)
}
return res
};
划分words,双指针
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
res = []
m, n, ls = len(words), len(words[0]), len(s)
for i in range(n):
if i + m * n > ls:
break
differ = Counter()
for j in range(m):
word = s[i + j * n: i + (j + 1) * n]
differ[word] += 1
for word in words:
differ[word] -= 1
if differ[word] == 0:
del differ[word]
for start in range(i, ls - m * n + 1, n):
if start != i:
word = s[start + (m - 1) * n: start + m * n]
differ[word] += 1
if differ[word] == 0:
del differ[word]
word = s[start - n: start]
differ[word] -= 1
if differ[word] == 0:
del differ[word]
if len(differ) == 0:
res.append(start)
return res
时间复杂度Omn 空间复杂度Omn
- 思路描述
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
res = []
m, n, ls = len(words), len(words[0]), len(s)
for i in range(n):
if i + m * n > ls:
break
differ = Counter()
for j in range(m):
word = s[i + j * n: i + (j + 1) * n]
differ[word] += 1
for word in words:
differ[word] -= 1
if differ[word] == 0:
del differ[word]
for start in range(i, ls - m * n + 1, n):
if start != i:
word = s[start + (m - 1) * n: start + m * n]
differ[word] += 1
if differ[word] == 0:
del differ[word]
word = s[start - n: start]
differ[word] -= 1
if differ[word] == 0:
del differ[word]
if len(differ) == 0:
res.append(start)
return res
- 时间复杂度:
- 空间复杂度:
from collections import Counter
from collections import defaultdict
c = Counter(words)
m = len(words)
n = len(words[0])
ret = []
total_length = m * n
#Loop over word length
for k in xrange(n):
left = k
subd = defaultdict(int)
count = 0
for j in xrange(k, len(s) - n + 1, n):
word = s[j:j+n]
if word in c:
subd[word] += 1
count += 1
while subd[word] > c[word]:
subd[s[left:left+n]] -= 1
left += n
count -= 1
if count == m:
ret.append(left)
else:
left = j + n
subd = defaultdict(int)
count = 0
return ret
// Lee Error some examples paased
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
unordered_map<string, int>judge;
vector<int>ans;
int len = 0;
int wordsLen = words.size();
if(words.size())
len = words[0].size();
else return{};
for (int i = 0; i < wordsLen; i++)
{
judge[words[i]] = 1;
}
wordsLen = 0;
for (auto k = judge.begin(); k != judge.end(); k++)
wordsLen += 1;
for (int i = 0; i < s.size() - len + 1; i++)
{
int flag = 1;
for (int j = i, k = 0; j < s.size() - len + 1 && k <wordsLen; j += len, k+=1)
{
string tmp = s.substr(j, len);
if (judge.count(tmp) > 0 && judge[tmp])
{
//judge[tmp] = 0;
}
else
{
flag = 0;
break;
}
}
if (flag)
ans.emplace_back(i);
for (auto k = judge.begin(); k != judge.end(); k++)
k->second = 1;
}
return ans;
}
};
时间复杂度:O(N*N)
空间复杂度: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;
}
}
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"] 输出:[]