Open azl397985856 opened 2 years ago
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
if (words.empty()) return res;
int m = words.size(), w = words[0].size(), mw = m * w, cnt = 0;
unordered_map<string, int> wf, wd;
for (auto& word : words) wf[word]++;
for (int i = 0; i < w; i++) {
for (int j = i; j + w <= s.size(); j += w) {
if (j >= i + mw) {
string word = s.substr(j - mw, w);
wd[word]--;
if (wd[word] < wf[word]) cnt--;
}
string word = s.substr(j, w);
wd[word]++;
if (wd[word] <= wf[word]) cnt++;
if (cnt == m) res.push_back(j - (m - 1) * w);
}
wd.clear();
cnt = 0;
}
return res;
}
};
TC: O(N*Len) SC: O(N)
def findSubstring(self, s: str, words: List[str]) -> List[int]:
wds_count = len(words)
wd_len = len(words[0])
lenght = wds_count * wd_len
wordCnt = defaultdict(int)
for word in words:
wordCnt[word] += 1
def is_concatenation(i):
# corner case: 可以重复的
count = wds_count
wordFreq = dict(wordCnt)
for x in range(wds_count):
start = i + x * wd_len
wd = s[start: start + wd_len]
if wd in wordFreq:
wordFreq[wd] -= 1
if wordFreq[wd] >= 0:
count -= 1
return count == 0
ptr = 0
res = []
while ptr <= len(s) - lenght:
if is_concatenation(ptr):
res.append(ptr)
ptr += 1
return res
思路:参考了题解
vector<int> findSubstring(string s, vector<string>& words)
{
vector<int> res;
int wordNum=words.size();
int wordLen=words[0].size();
unordered_map<string,int> allWords;
for(string it:words)
allWords[it]++;
for(int i=0;i<s.size()-wordLen*wordNum+1;i++)
{
unordered_map<string,int> window;
int num=0;
while(num<wordNum)
{
string word=s.substr(i+num*wordLen,wordLen);
if(allWords[word]==0) break;
else
{
window[word]++;
if(window[word]>allWords[word]) break;
}
num++;
}
if(num==wordNum) res.push_back(i);
}
return res;
}
时间复杂度:O(n+len) 空间复杂度:O(n)
#include <string>
#include <vector>
#include <unordered_map>
using namespace std;
class Solution {
public:
unordered_map<string, int> map;
int totalLen;
int oneWordLen;
int finalProgress;
vector<int> res;
vector<int> findSubstring(string s, vector<string>& words) {
for(string& str : words)
map[str]++;
oneWordLen = words[0].size();
totalLen = oneWordLen * words.size();
finalProgress = words.size();
if(s.size() < totalLen)
return res;
//to try to constrcut the target string
for(int i = 0; i <= s.size() - totalLen; i++){
dfs(s, i, 0);
}
return res;
}
void dfs(string& s, int curPos, int progress){
if(progress == finalProgress){
res.push_back(curPos - totalLen);
return;
}
string frac = s.substr(curPos, oneWordLen);
if(map[frac] == 0)
return;
map[frac]--;
dfs(s, curPos + oneWordLen, progress + 1);
map[frac]++;
}
};
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
n = len(s)
k = len(words)
word_length = len(words[0])
substring_size = word_length * k
word_count = collections.Counter(words)
def sliding_window(left):
words_found = collections.defaultdict(int)
words_used = 0
excess_word = 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 right in range(left, n, word_length):
if right + word_length > n:
break
sub = s[right : right + word_length]
if sub not in word_count:
# Mismatched word - reset the window
words_found = collections.defaultdict(int)
words_used = 0
excess_word = False
left = right + word_length # Retry at the next index
else:
# If we reached max window size or have an excess word
while right - left == substring_size or excess_word:
# Move the left bound over continously
leftmost_word = s[left : left + word_length]
left += word_length
words_found[leftmost_word] -= 1
if words_found[leftmost_word] == word_count[leftmost_word]:
# This word was the excess word
excess_word = False
else:
# Otherwise we actually needed it
words_used -= 1
# Keep track of how many times this word occurs in the window
words_found[sub] += 1
if words_found[sub] <= word_count[sub]:
words_used += 1
else:
# Found too many instances already
excess_word = True
if words_used == k and not excess_word:
# Found a valid substring
answer.append(left)
answer = []
for i in range(word_length):
sliding_window(i)
return answer
Refer to solutions: use hashmap to record count of each word, and iterate every n length of word to update the seen dictionary.
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if len(s) == 0 or words == []:
return []
k = len(words)
n = len(words[0])
ans = []
count = Counter(words)
for i in range(len(s) - k * n + 1):
seen = defaultdict(int)
j = 0
while j < k:
word = s[i + j * n: i + j * n + n]
seen[word] += 1
if seen[word] > count[word]:
break
j += 1
if j == k:
ans.append(i)
return ans
O(a*n) time, O(a+b) space
用哈希表保存words中字符串的出现次数。然后暴力枚举的s中的每个位置,看能否构成满足题意的字符串。
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
HashMap<String,Integer> map = new HashMap<>();
int n = s.length();
int sl = words[0].length();
List<Integer> res = new ArrayList<>();
if (n < sl){
return new ArrayList<>();
}
for (String str : words) {
map.put(str,map.getOrDefault(str,0)+1);
}
for (int i = 0; i < n; i++) {
int x = i;
if (i + sl * words.length > n)
break;
String str = s.substring(x,x+sl);
HashMap<String,Integer> tmp = new HashMap<>(map);
while (!tmp.isEmpty() && tmp.containsKey(str)) {
x = x + sl;
tmp.put(str,tmp.get(str) - 1);
if (tmp.get(str) == 0){
tmp.remove(str);
}
if (x + sl > n) {
break;
}
str = s.substring(x,x+sl);
}
if (tmp.isEmpty()){
res.add(i);
}
}
return res;
}
}
思路:滑动窗口法:一直在 s
维护着所有单词长度总和的一个长度队列!
代码:
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])
word_num = len(words)
n = len(s)
words = Counter(words)
res = []
for i in range(0, one_word):
cur_cnt = 0
left = i
right = i
cur_Counter = Counter()
while right + one_word <= n:
w = s[right:right + one_word]
right += one_word
cur_Counter[w] += 1
cur_cnt += 1
while cur_Counter[w] > words[w]:
left_w = s[left:left+one_word]
left += one_word
cur_Counter[left_w] -= 1
cur_cnt -= 1
if cur_cnt == word_num :
res.append(left)
return res
时间复杂度:O(n)
滑动窗口,两个哈希表,一个记录words,一个记录滑动子串
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
if(words.size()==0){
return {};
}
int word_len=words[0].size();
int word_num=words.size();
//滑动窗口长度
int win_len=word_len*word_num;
//哈希表m1记录words
unordered_map<string,int> m1;
for(string num:words){
m1[num]++;
}
for(int i=0;i<s.size()-win_len+1;i++){
//哈希表m2记录滑动子串
unordered_map<string,int> m2;
int j;
for(j=i;j<i+win_len;j+=word_len){
//利用string截取子字符串substr
string sub=s.substr(j,word_len);
m2[sub]++;
if(m1[sub]==0 || m1[sub]<m2[sub]){
break;
}
}
if(j==i+win_len){
res.push_back(i);
}
}
return res;
}
};
哈希表2:key() value()
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;//?????????????????
//字符串长度n,words中单词个数m,单词长度d
int n=s.size(), m=words.size(), d=words[0].size();
int len=m*d;//表示words数组所能拼接的字符串长度,即滑动窗口的长度。
//哈希表1,存所有单词及其出现次数
unordered_map<string,int> allwords;
for(string w:words){
allwords[w]++;//这里需要再看一下map原理,是怎么将单词加入到hashmap中的
}
//初始化0~d-1滑动窗口对应的词频统计表
vector<unordered_map<string, int>> sm(d);
for(int i=0; i<d&&i+len<=n ; i++){
//substr(j,d)复制子字符串,要求从指定位置j开始,并具有指定的长度d.并且每次增加单词长度d
//j=i开始!!!
for(int j = i ; j < i+len ; j+=d){
string str=s.substr(j,d);
sm[i][str]++;//???????????????
}
if(sm[i]==allwords){
res.push_back(i);//????????????
}
}
//移动滑动窗口,每次出一个单词,进一个单词
for(int i=d; i+len<=n ; i++){
int r=i%d;
string str1=s.substr(i-d,d);//出窗字符串
string str2=s.substr(i+len-d,d);//出窗字符串
if(--sm[r][str1]==0)
sm[r].erase(str1);
sm[r][str2]++;
if(sm[r]==allwords)
res.push_back(i);
}
return res;
}
};
hashmap + 暴力搜索
var findSubstring = function(s, words) {
if (!s || !words || !words.length) return [];
let wordLen = words[0].length // 单个字符的长度
let len = words.length // 单词的个数
let allWordsLen = wordLen*len
let ans = [], wordMap = {};
// 哈希表记录每个字符出现的次数
for (let w of words) {
wordMap[w] ? wordMap[w]++ :wordMap[w] = 1
}
for (let i = 0; i < s.length - allWordsLen + 1; i++) {
let wm = Object.assign({}, wordMap);
for (let j = i; j < i + allWordsLen - wordLen + 1; j += wordLen) {
let w = s.slice(j, j + wordLen);
if (wm[w]) {
wm[w]--
} else {
break;
}
}
if (Object.values(wm).every(n => n === 0)) ans.push(i);
}
return ans;
};
时间复杂度: O(mn) m为s长度,n为words长度
空间复杂度: O(m+n)
Python3 Code:
class Solution:
import copy
def Iterative_string(self,s:str,words,start,word_len):
res = []
tmp_words = copy.deepcopy(words)
if start+len(''.join(words)) > len(s):
return res
i = start
while s[start:start+word_len] in tmp_words:
tmp_words.remove(s[start:start+word_len])
start += word_len
if not tmp_words:
res.append(i)
while start + word_len <= len(s) and s[start:start+word_len] == s[i:i+word_len]:
res.append(i)
i += word_len
start += word_len
return res
def findSubstring(self, s: str, words: List[str]) -> List[int]:
"""
串联所有单词的子串
"""
res = []
left = 0
word_len = len(words[0])
for i in range(len(s)):
if i+word_len*len(words) <= len(s) and s[i:i+word_len] in words:
res.extend(self.Iterative_string(s,words,i,word_len))
else:
continue
return list(set(res))
复杂度分析
令 n 为数组长度。
思路和上面相似 但是实现时采用了哈希表来确定子串
Python3 Code:
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
复杂度分析
令 n 为数组长度。
string s 滑动窗口历变,判断是否可能由words组成
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
// Result is a list of int coordinates
List<Integer> result = new ArrayList<Integer>();
// Empty 情况
if (words == null || words[0].length() == 0) {
return result;
}
// 储存所查找word为key,value为出现次数
Map<String, Integer> wordsFreq = new HashMap<>();
for (String word : words) {
wordsFreq.put(word, wordsFreq.getOrDefault(word, 0) + 1);
}
// 思路:滑动窗口比较
int wordLength = words[0].length();
int wordListLength = words.length;
int targetLength = wordLength * wordListLength;
int stringLength = s.length();
// 历变全部可能的窗口
for (int i = 0; i < stringLength - targetLength + 1; i++) {
String cur = s.substring(i, i + targetLength);
int length = 0;
Map<String, Integer> wordsFreqTmp = new HashMap<>();
// 每一个窗口都去比较是否由目标words组成
for (int j = 0; j < cur.length(); j = j + wordLength) {
String part = cur.substring(j, j + wordLength);
// 不包含目标words的情况
if (!wordsFreq.containsKey(part)) {
break;
}
wordsFreqTmp.put(part, wordsFreqTmp.getOrDefault(part, 0) + 1);
// 目标words过多情况
if (wordsFreqTmp.get(part) > wordsFreq.get(part)) {
break;
}
length = length + wordLength;
}
// 若上面的标准都通过,则为由目标words组成的string,窗口有效
if (length == targetLength) {
result.add(i);
}
}
return result;
}
}
class Solution {
public List
if (n < sl){
return new ArrayList<>();
}
for (String str : words) {
map.put(str,map.getOrDefault(str,0)+1);
}
for (int i = 0; i < n; i++) {
int x = i;
if (i + sl * words.length > n)
break;
String str = s.substring(x,x+sl);
HashMap<String,Integer> tmp = new HashMap<>(map);
while (!tmp.isEmpty() && tmp.containsKey(str)) {
x = x + sl;
tmp.put(str,tmp.get(str) - 1);
if (tmp.get(str) == 0){
tmp.remove(str);
}
if (x + sl > n) {
break;
}
str = s.substring(x,x+sl);
}
if (tmp.isEmpty()){
res.add(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 []
one_word=len(words[0]) # 一个字符的长度
word_num=len(words) # words中一共有多少个字符
n=len(s)
words=Counter(words)
res=[]
for i in range(0,one_word): # 这里的range范围为什么是(0,one_word)?
cur_cnt=0
left=i
right=i
cur_Counter=Counter()
while right+one_word<=n:
w=s[right:right+one_word]
right+=one_word # right加上一个one_word的长度
if w not in words:
left=right
cur_Counter.clear()
cur_cnt=0
else:
cur_Counter[w]+=1 # 用Counter来计数
cur_cnt+=1 # 组成cur_Counter中的所有单词个数
while cur_Counter[w]>words[w]:
left_w=s[left:left+one_word]
left+=one_word
cur_Counter[left_w]-=1
cur_cnt-=1
if cur_cnt==word_num:
res.append(left)
return res
时间复杂度:O(n)
空间复杂度:O(n)
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;
HashMap<String, Integer> map = new HashMap<>();
int one_word = words[0].length();
int word_num = words.length;
int all_len = one_word * word_num;
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
for (int i = 0; i < s.length() - all_len + 1; i++) {
String tmp = s.substring(i, i + all_len);
HashMap<String, Integer> tmp_map = new HashMap<>();
for (int j = 0; j < all_len; j += one_word) {
String w = tmp.substring(j, j + one_word);
tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
}
if (map.equals(tmp_map)) res.add(i);
}
return res;
}
time/space complexity: 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;
}
}
JavaScript Code:
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
let map = new Map()
words.forEach(o => map.set(o, (map.get(o) || 0) + 1))
const n = words.length
const len = words[0].length
const res = []
for (let i = 0; i <= s.length - n * len; i++) {
let subMap = {}
let num = 0
while (num < n) {
let cur = s.substr(i + num * len, len)
if (!map.has(cur) || subMap[cur] === map.get(cur)) break
subMap[cur] = (subMap[cur] || 0) + 1
num++
}
if (num === n) {
res.push(i)
}
}
return res
};
复杂度分析
令 n 为数组长度。
var findSubstring = function(s, words) { const wordLen = words[0].length; const wLen = words.length; let res = []; let map = new Map(); for (let i = 0; i < wLen; i++) { map.set(words[i] , (map.get(words[i]) || 0) + 1); }
let right = 0;
const n = s.length;
while (right < n) {
let left = right;
const subWord = new Map();
while (left < right + wLen * wordLen) {
const cur = s.substr(left, wordLen);
if (!map.has(cur) || subWord.get(cur) == map.get(cur)) {
break;
}
subWord.set(cur, (subWord.get(cur) || 0) + 1);
left += wordLen;
}
if (left == right + wLen * wordLen) {
res.push(right);
}
right++;
}
return res;
};
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if len(words) == 0 or len(s) == 0:
return []
len_word = len(words[0])
res = []
for i in range(len(s) - len_word * len(words) + 1):
copy_words = words.copy()
k = i
while len(copy_words) != 0:
if s[k : k+len_word] in copy_words:
copy_words.remove(s[k : k+len_word])
k += len_word
else:
break
if len(copy_words) == 0:
res.append(i)
return res
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
int len = s.length();
int wordsNum = words.length;
int wordLen = words[0].length();
Map<String, Integer> map = new HashMap<>();
List<Integer> res = new LinkedList<>();
for (int i = 0; i < wordsNum; i++) {
map.put(words[i], map.getOrDefault(words[i], 0) + 1);
}
for (int i = 0; i < len - wordLen * wordsNum + 1; i++) {
int left = i;
Map<String, Integer> tmp = new HashMap<>();
int count = 0;
while (left + wordLen <= len) {
String word = s.substring(left, left + wordLen);
if (!map.containsKey(word)) {
break;
}
tmp.put(word, tmp.getOrDefault(word, 0) + 1);
if (tmp.get(word) > map.get(word)) {
break;
}
count++;
left += wordLen;
}
if (count == wordsNum) {
res.add(i);
}
}
return res;
}
}
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
int len = s.length();
int wordsNum = words.length;
int wordLen = words[0].length();
Map<String, Integer> map = new HashMap<>();
List<Integer> res = new LinkedList<>();
for (int i = 0; i < wordsNum; i++) {
map.put(words[i], map.getOrDefault(words[i], 0) + 1);
}
for (int i = 0; i < len - wordLen * wordsNum + 1; i++) {
int left = i;
Map<String, Integer> tmp = new HashMap<>();
int count = 0;
while (left + wordLen <= len) {
String word = s.substring(left, left + wordLen);
if (!map.containsKey(word)) {
break;
}
tmp.put(word, tmp.getOrDefault(word, 0) + 1);
if (tmp.get(word) > map.get(word)) {
break;
}
count++;
left += wordLen;
}
if (count == wordsNum) {
res.add(i);
}
}
return res;
}
}
参考CSDN答案 先使用一个字典统计一下words中每个单词的数量。由于每个单词的长度一样,以题中给的例子而言,可以3个字母3个字母的检查,如果不在字典中,则break出循环。有一个技巧是建立一个临时字典curr,用来统计S中那些在L中的单词的数量,必须和L中单词的数量相等,否则同样break
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
res, dict = [] ,{}
n_word = len(words)
n_s = len(s)
if words:
word_length = len(words[0])
else:
return res
# 统计单词出现的次数
for i in range(n_word):
if words[i] in dict:
dict[words[i]] +=1
else:
dict[words[i]] = 1
#关于这个for里面的+1,你可以假设length_s与word_num * length_word一样那么很明显应该进入循环判断
for i in range(n_s - n_word*word_length + 1):
#curr用来统计小循环内出现相同单词次数,如果大于单词组内该单词出现数直接break
curr,j = {},0
#小循环,j相当于指向一个单词,每次移动一个单词,截取s内一个单词的长度判断是都在dict中
while j < n_word:
word = s[i+j*word_length : i+j*word_length + word_length]
if not word in dict: break
if not word in curr:
curr[word] = 1
else:
curr[word] +=1
if curr[word] > dict[word]:
break
j+=1
#出小循环后j(单词数)等于单词数组数那就是刚好匹配,保存i
if j == n_word:
res.append(i)
return res
class Solution {
private HashMap<String, Integer> wordCount = new HashMap<String, Integer>();
private int wordLength;
private int substringSize;
private int k;
private boolean check(int i, String s) {
// Copy the original dictionary to use for this index
HashMap<String, Integer> remaining = new HashMap<>(wordCount);
int wordsUsed = 0;
// Each iteration will check for a match in words
for (int j = i; j < i + substringSize; j += wordLength) {
String sub = s.substring(j, j + wordLength);
if (remaining.getOrDefault(sub, 0) != 0) {
remaining.put(sub, remaining.get(sub) - 1);
wordsUsed++;
} else {
break;
}
}
return wordsUsed == k;
}
public List<Integer> findSubstring(String s, String[] words) {
int 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 < n - substringSize + 1; i++) {
if (check(i, s)) {
answer.add(i);
}
}
return answer;
}
}
class Solution {
public List
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
n = len(s)
one_word_len = len(words[0]) #一个单词的长度
word_cnt = len(words) #单词的个数
all_len = one_word_len * word_cnt
words_cnt = defaultdict(int) #每个单词出现的次数
for w in words:
words_cnt[w] += 1
res = []
for i in range(one_word_len): #shell希尔排序的套路 start delta = 一个单词的长度
cur_w_num = 0 #单词个数
L, R = i, i
cur_w_dic = defaultdict(int) #当前单词的次数统计
while R + one_word_len <= len(s):
w = s[R: R + one_word_len]
R += one_word_len #R每次像右移一个单词的长度 类似于shell排序中的delta
if w not in words_cnt: #结束前,出现个不知道哪里的单词,整个前面一片就废了
L = R
cur_w_dic.clear()
cur_w_num = 0
else:
cur_w_dic[w] += 1
cur_w_num += 1
while cur_w_dic[w] > words_cnt[w]:
L_w = s[L : L + one_word_len]
L += one_word_len #L右移
cur_w_dic[L_w] -= 1 #做好统计
cur_w_num -= 1
if cur_w_num == word_cnt: #如果说ok了
res.append(L)
return res
Sliding window.
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;
HashMap<String, Integer> map = new HashMap<>();
int wordLen = words[0].length();
int size = words.length;
int len = wordLen * size;
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
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 (!map.containsKey(r)) {
count = 0;
left = right;
window.clear();
} else {
window.put(r, window.getOrDefault(r, 0) + 1);
count++;
while (window.getOrDefault(r, 0) > map.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
class Solution {
public List
// 同步移动 看窗口是否满足
while(r < sLen) { // 左闭右开 r不可达 因此r可到最大 + 1 仅仅作为不可达边界
wordInWin = new HashMap<>();
l ++;
r ++;
int tmp = l;
while(tmp < r) {
String w = s.substring(tmp, tmp + step);
wordInWin.put(w, wordInWin.getOrDefault(w, 0) + 1);
tmp += step;
}
if(match(wordInWin, target)) res.add(l); // 每次移动完判断一次
}
return res;
}
private boolean match(Map<String, Integer> window, Map<String, Integer> target) {
for(String word : target.keySet()) {
if(!target.get(word).equals(window.get(word)) ) return false;
}
return true;
}
}
先用一个map存储words中的字符,字符为key,数量为value。计算words总长度 words[0].length*words.length。
遍历字符串s,从当前位置left开始截取长度为words总长度的字符串y,将y按words[0]的长度分解出单词,将分解出的单词存储到map2中。
比较map和map2中每个单词的数量。
var findSubstring = function(s, words) {
let map = new Map(), map2 = new Map();
for (let i = 0; i < words.length; i++) {
if (map.has(words[i])) map.set(words[i], map.get(words[i]) + 1);
else map.set(words[i], 1);
}
let single = words[0].length;
let wordsLength = single * words.length;
let left = 0, res = [];
while (left < s.length - wordsLength + 1) {
let end = wordsLength + left, start = left;
map2.clear();
for (; start < end; start += single) {
let key = s.substr(start, single);
if (!map.has(key)) break;
if (map2.has(key)) map2.set(key, map2.get(key) + 1);
else map2.set(key, 1);
if (map2.get(key) > map.get(key)) break;
}
if (start === end) res.push(left);
left++;
}
return res;
};
time: O(n m k) n: s的长度,m: words的长度,k: 单个word的长度
space: O(m), m为words长度
计算 words 段的总长度,根据 words 维护 hash 表,需要在目标 string 里按着 words 总长度去遍历子串,判断子串里是否存在 hash 表的字段,若存在,hash 表中对应字段值减 1,直到对应字段值为 0,表示该字段已经用完;若不存在,则放弃该子串,右移遍历下一个子串
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;
};
串联所有单词的子串 https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/comments/
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if(s.empty() || words.empty()) return {};
int n = words.size(), m = words[0].size(),j=0;
if(s.size() < m*n) return {};
vector<int> res;
unordered_map<string,int>mp,tmp;
for(auto str: words){
mp[str]++;
}
string str = "";
for(int i = 0; i+m*n<= s.size();i++){
for( j = i;j<i+m*n;j+=m){
str = s.substr(j,m);
if(mp.find(str) == mp.end()) break;
tmp[str]++;
}
if(j == i+m*n && tmp == mp) res.push_back(i);
tmp.clear();
}
return res;
}
};
时间复杂度:O(n^m) 空间复杂度:O(n)
定义一个map,存储单词和出现个数。遍历字符串s,只能一位一位的移动。左窗口遍历的下标,右窗口为i+目标字符串数组的字符长度。然后在里面再裁剪,并存储到临时map,这是为了记录单词出现个数,防止单词超出需要的个数。结束以后,当左窗口能到达右边时,则表示该部分是目标字符串。
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> result = new ArrayList<>();
if (s == null || s.length() == 0 || words.length == 0) {
return result;
}
//单个单词长度
int oneWordLength = words[0].length();
//单词个数
int wordNum = words.length;
//总长度
int allWordsLength = oneWordLength * wordNum;
HashMap<String, Integer> wordMap = new HashMap<>();
//记录每个单词出现次数
for (String word : words) {
wordMap.put(word, wordMap.getOrDefault(word, 0) + 1);
}
//开始遍历
for (int i = 0; i < s.length() - allWordsLength + 1; i++) {
//建立临时map
HashMap<String, Integer> tmp = new HashMap<>();
//定义左指针
int leftSide = i;
int rightSide = leftSide + allWordsLength;
//判断窗口内
while (leftSide < rightSide) {
//裁剪单词
String compareWord = s.substring(leftSide, leftSide + oneWordLength);
//如果不存在,右移窗口
if (!wordMap.containsKey(compareWord)) {
break;
}
//如果单词超过了,抛弃
if (tmp.get(compareWord)==wordMap.get(compareWord)) {
break;
}
//存储到临时map,这里记录个数是为了比较单词数是否超出
tmp.put(compareWord, tmp.getOrDefault(compareWord, 0) + 1);
//窗口移动一个单词长度
leftSide+=oneWordLength;
}
//如果窗口能够移动到右边界(整段都符合)
if (leftSide==i+allWordsLength){
result.add(i);
}
}
return result;
}
}
时间复杂度O(n^2)
空间复杂度O(n)
// 4-23 cpp
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if (s.empty() || words.empty()) return {};
int len = s.size();
unordered_map<string, int> mp;
int a = 0;
vector<int> ans;
for (auto w : words) {
++mp[w];
}
int wlen = words[0].size(), count = words.size();
int match = 0;
for (int i = 0; i < len - wlen * count + 1; i++) {
string cur = s.substr(i, wlen * count);
unordered_map<string, int> temp;
int j = 0, cnt = 0;
for (; j < cur.size(); j += wlen) {
string curword = cur.substr(j, wlen);
if (mp.count(curword) == 0) break;
temp[curword]++;
cnt++;
if (temp[curword] > mp[curword]) break;
if (cnt == count) ans.push_back(i);
}
}
return ans;
}
};
如果子串的字符数能够满足要求,判断里面的单词是否符合要求
将words保存到hash1表中,key为word,value为出现的次数
遍历子串s,遍历每个单词,保存至hash2表中。
class Solution {
public:
bool IsVectorEque(const string& s) {
//cout << "s = " << s << endl;
std::map<string, int> hash2;
for (int i =0 ;i <= s.size() - word_len; i+=word_len) {
string sub_str = s.substr(i, word_len);
// cout << "sub_str = " << sub_str << endl;
if (hash1.find(sub_str) != hash1.end()) { // 单词次数大于当前已有次数
hash2[sub_str] ++;
if (hash2[sub_str] > hash1[sub_str] ) {
// cout << "s = " << s << " key > " << sub_str << endl;
return false;
}
} else { // 单词不存在
// cout << "s = " << s << " key not in " << sub_str << endl;
return false;
}
}
return true;
}
vector<int> findSubstring(string s, vector<string>& words) {
// 字符串的hash表
int word_num = 0;
if (words.size() > 0) {
word_len = words[0].size();
}
for (const auto& word : words) {
hash1[word]++;
word_num += word_len;
}
std::vector<int> ret_vec;
if (s.size() < word_num) {
return ret_vec;
}
for (int i =0 ; i<= s.size() - word_num; i++) {
if (IsVectorEque(s.substr(i, word_num))) {
ret_vec.emplace_back(i);
}
}
return ret_vec;
}
private:
std::map<string, int> hash1; // 单词与频次的hash表
int word_len;
};
时间:O(N2)
空间: O(N)
很简单的,直接用一个数组来存储就行
<?php
class MyHashSet {
/**
* @var array
*/
private $hash;
/**
*/
function __construct() {
$this->hash = [];
}
/**
* @param Integer $key
* @return NULL
*/
function add($key) {
$this->hash[$key] = '';
return null;
}
/**
* @param Integer $key
* @return NULL
*/
function remove($key) {
unset($this->hash[$key]);
return null;
}
/**
* @param Integer $key
* @return Boolean
*/
function contains($key) {
return isset($this->hash[$key]);
}
}
/**
* Your MyHashSet object will be instantiated and called as such:
* $obj = MyHashSet();
* $obj->add($key);
* $obj->remove($key);
* $ret_3 = $obj->contains($key);
*/
时间复杂度:O(n) 空间复杂度:O(n)
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/
给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
示例 3:
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
提示:
1 <= s.length <= 104
s 由小写英文字母组成
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 由小写英文字母组成
使用滑动窗口,当窗口的大小和words的长度一致时,使用map存储该窗口根据单词长度划分的单词,
将map和words构成的map进行对比,满足要求则输出left。
JavaScript Code:
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
let left = 0, right = 0;
let slen = s.length;
let wordLen = words[0].length;
let wordNum = words.length;
let wlen = wordNum * wordLen;
let wordMap = new Map();
for (let word of words) {
let count = wordMap.has(word) ? wordMap.get(word) : 0;
wordMap.set(word, count + 1);
}
let res = [];
while (right < slen) {
right++;
if (right - left === wlen) {
if (match(s.substring(left, right), wordMap, wordNum, wordLen)) {
res.push(left);
}
left++;
}
}
return res;
};
function match(str, wordMap, wordNum, wordLen) {
let map = new Map();
for (let i = 0; i < wordNum; i++) {
let word = str.substring(i * wordLen, (i + 1) * wordLen);
let count = map.has(word) ? map.get(word) : 0;
map.set(word, count + 1);
}
let matchflag = true;
for (let [key, value] of wordMap) {
if (!map.has(key) || map.get(key) !== value) {
matchflag = false;
}
}
return matchflag;
}
复杂度分析
令 n 为数组长度。
To make this question a bit easier, we can first transform the input string into something more friendly for data handling —— hash map
As the output order is not important, I will use hash maps here.
- Transform「words」into a hash map A
- Split the input string into substrings and store each word of the substrings in hash map B
- Match hash map A and B
- if matched, store the index of each substring's first letter
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 {
public:
vector
Space:O(n), Time:O(n)
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
if (!words || !words.length) return[];
let wordLen = words[0].length;
let allWordsLen = wordLen * words.length;
let ans = [], wordMap = {};
for (let w of words) {
wordMap[w] ? wordMap[w]++ :wordMap[w] = 1
}
for (let i = 0; i < s.length - allWordsLen + 1; i++) {
let wm = Object.assign({}, wordMap);
for (let j = i; j < i + allWordsLen - wordLen + 1; j += wordLen) {
let w = s.slice(j, j + wordLen);
if (wm[w]) {
wm[w]--
} else {
break;
}
}
if (Object.values(wm).every(n => n === 0)) ans.push(i);
}
return ans;
};
map存储words的单词统计个数,然后再字符串寻找排列组合,由于words中的单词长度固定,每一个可以以固定长度增长左右指针。
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
from collections import Counter
if not s or not words:return []
word_length = len(words[0])
word_counts = len(words)
n = len(s)
if n < word_length:return []
words_map = Counter(words)
res = []
for i in range(0, word_length):
cur_cnt = 0
left = i
right = i
cur_Counter = Counter()
while right + word_length <= n:
w = s[right:right + word_length]
right += word_length
if w not in words_map:
left = right
cur_Counter.clear()
cur_cnt = 0
else:
cur_Counter[w] += 1
cur_cnt += 1
while cur_Counter[w] > words_map[w]:
left_w = s[left:left+word_length]
left += word_length
cur_Counter[left_w] -= 1
cur_cnt -= 1
if cur_cnt == word_counts :
res.append(left)
return res
时间复杂度:O(N)
空间复杂度:O(1)
滑动窗口 + 哈希表
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:return []
one_word = len(words[0])
word_num = len(words)
n = len(s)
words = Counter(words)
res = []
for i in range(0, one_word):
cur_cnt = 0
left = i
right = i
cur_Counter = Counter()
while right + one_word <= n:
w = s[right:right + one_word]
right += one_word
cur_Counter[w] += 1
cur_cnt += 1
while cur_Counter[w] > words[w]:
left_w = s[left:left+one_word]
left += one_word
cur_Counter[left_w] -= 1
cur_cnt -= 1
if cur_cnt == word_num :
res.append(left)
return res
O(n)
O(n)
思路 暴力解有两种思路: 1、从words入手,排序组合生成xx种字符串,查看各个字符串在s中的位置 排序组合的时间开销是 (words.length)! / (words 中单词重复次数相乘), 时间复杂度为 O(m!), m 为 words 长度。阶乘的时间复杂度基本不可能通过 2、从s入手,遍历每个长度为 words.lengthlen(words[0]) 的字符串,转为子问题,判断长度为 words.lengthlen(words[0]) 的字符串是否可以由words组成 关键问题:如何判断 s 的子串 Y 是否可以由 words 数组的构成
words数组中单词长度不同
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
need_dic = collections.defaultdict(int)
for word in words:
need_dic[word] += 1
words_total = len(words) * len(words[0])
i, j = 0, words_total
result = []
while i < len(s) and j <= len(s):
cur_words = s[i : j + 1]
if self.is_same_words(cur_words, words, need_dic):
result.append(i)
i += 1
j = i + words_total
return result
def is_same_words(self, cur_words, words, need_dic):
find_dic = collections.defaultdict(int)
m = 0
length = len(words[0])
for index in range(0, len(cur_words), length):
cur_word = cur_words[index : index + length]
if cur_word in need_dic:
find_dic[cur_word] += 1
if find_dic[cur_word] == need_dic[cur_word]:
m += 1
if find_dic[cur_word] > need_dic[cur_word]:
break
else:
break
if m == len(need_dic):
return True
return False
时间复杂度 o(nmk) ,n为s长度,m为words数组长度,k为单词长度 空间复杂度 o(m)
Idea:暴力穷举
Code: ''' var findSubstring = function(s, words) { if (!words || !words.length) return[]; let wordLen = words[0].length; let allWordsLen = wordLen * words.length; let ans = [], wordMap = {}; for (let w of words) { wordMap[w] ? wordMap[w]++ :wordMap[w] = 1 } for (let i = 0; i < s.length - allWordsLen + 1; i++) { let wm = Object.assign({}, wordMap); for (let j = i; j < i + allWordsLen - wordLen + 1; j += wordLen) { let w = s.slice(j, j + wordLen); if (wm[w]) { wm[w]-- } else { break; } } if (Object.values(wm).every(n => n === 0)) ans.push(i); } return ans; }; ''' 时间复杂度O(n) 空间复杂度O(n)
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int sn = s.size(), n = words.size(), m = words[0].size();
unordered_map<string, int> wc;
vector<int> ans;
for(auto& w : words) wc[w]++;
for(int i = 0; i < m; ++i){
int l = i, r = i, num = 0;
unordered_map<string, int> wc0;
while(r + m <= sn){
auto back = s.substr(r, m);
r += m;
if(!wc.count(back)){
l = r;
num = 0;
wc0.clear();
} else {
num++;
wc0[back]++;
while(wc[back] < wc0[back]){
auto front = s.substr(l, m);
wc0[front]--;
num--;
l += m;
}
if(num == n) ans.push_back(l);
}
}
}
return ans;
}
};
使用两个指针
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 + wordLen * wordNum:
res.append(i)
return res
时间 O(n∗m),n为字符串长度,m为单词个数 \ 空间 O(m)
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/
给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
示例 3:
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
提示:
1 <= s.length <= 104
s 由小写英文字母组成
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 由小写英文字母组成
JavaScript Code:
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
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;
};
复杂度分析
令 n 为数组长度。
4月23日
【day23】
难度困难648
给定一个字符串 s
和一些 长度相同 的单词 words
。找出 s
中恰好可以由 words
中所有单词串联形成的子串的起始位置。
注意子串要与 words
中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words
中单词串联的顺序。
示例 1:
输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
滑动窗口思想,因为word长度相同,维护一个『word长度』* 『words长度』的窗口,判断窗口内的words与输入的words是否相同,这点可以使用HashMap。
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList();
int wordLen = words[0].length();
int len = wordLen*words.length;
HashMap<String,Integer> need = new HashMap();
HashMap<String,Integer> window = new HashMap();
for(String ss : words){
need.put(ss,need.getOrDefault(ss,0)+1);
}
if(s.length() < len){
return res;
}
int left = 0;
int right = left+len;
int vaild = 0;
while(right <= s.length()){
for(int i = 1;i <= words.length;i++){
String tmp = s.substring(left+(i-1)*wordLen,left+i*wordLen);
if(!need.containsKey(tmp)){
break;
}else{
window.put(tmp,window.getOrDefault(tmp,0)+1);
if(need.get(tmp).equals(window.get(tmp))){
vaild++;
}
}
}
if(vaild == need.size()){
res.add(left);
}
window.clear();
vaild = 0;
left++;
right++;
}
return res;
}
}
滑动窗口
var findSubstring = function(s, words) {
let left = 0,right = 0,wordsLen = words.length;
if(wordsLen == 0) return [];
let res = [];
let gapLen = words[0].length;
let needs = {};
let windows = {};
for(let i = 0;i < wordsLen;i++){
needs[words[i]] ? needs[words[i]]++ : needs[words[i]] = 1;
}
let needsLen = Object.keys(needs).length;
let match = 0;
for(let i = 0;i < gapLen;i++){
right = left = i;
match = 0;
while(right <= s.length - gapLen){
let c1 = s.substring(right,right + gapLen);
right += gapLen;
windows[c1] ? windows[c1]++ : windows[c1] = 1;
if(windows[c1] === needs[c1]){
++match;
}
while(left < right && match == needsLen){
if(Math.floor((right - left) / gapLen) == wordsLen){
res.push(left);
}
let c2 = s.substring(left,left + gapLen);
left += gapLen;
windows[c2]-- ;
if(needs[c2] && windows[c2] < needs[c2]){
match--;
}
}
}
windows = {};
}
return res;
};
时间复杂度O(n) 空间复杂度O(n)
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;
HashMap<String, Integer> map = new HashMap<>();
int one_word = words[0].length();
int word_num = words.length;
int all_len = one_word * word_num;
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
for (int i = 0; i < s.length() - all_len + 1; i++) {
String tmp = s.substring(i, i + all_len);
HashMap<String, Integer> tmp_map = new HashMap<>();
for (int j = 0; j < all_len; j += one_word) {
String w = tmp.substring(j, j + one_word);
tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
}
if (map.equals(tmp_map)) res.add(i);
}
return res;
}
}
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
if (words.empty()) return 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;
}
};
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"] 输出:[]