Open azl397985856 opened 2 years ago
预设几个变量 words_len: words数组的长度; word_len: words中每个字符串的长度
步骤
创建1个map, map的key为words中的每个word, map的value为word在words中出现的次数
创建2个索引, left和right, left默认为0, left与right间保持一个定量的关系: right = left + words_len * word_len
从s中截取[left, right]部分的字符串, 把这部分的字符串均分成words_len份, 然后把每部分的子串存进一个新的map中, map的key是长度为word_len的子串, value是子串在[left, right]中出现的次数
比对2个map, 如果2个map中key和value都是一致的, 就可以认为是有效的答案, 然后把left存进结果数组, left和right分别加1
在right没到达s的尾部时, 循环第步的过程
var findSubstring = function(s, words) {
const ans = []
const singleWordLen = words[0].length
const windowWidth = singleWordLen * words.length
const wordsMap = new Map()
words.forEach((word) => {
wordsMap.set(word, (wordsMap.get(word) || 0) + 1)
})
let left = 0
let right = windowWidth - 1
while(right < s.length) {
const subStrMap = new Map()
let isValid = true
for(let i = left; i <= right - singleWordLen + 1; i += singleWordLen) {
const subStr = s.slice(i, i + singleWordLen)
if (wordsMap.get(subStr) === undefined) {
isValid = false
break
}
subStrMap.set(subStr, (subStrMap.get(subStr) || 0) + 1)
}
if (isValid) {
for(let key of subStrMap.keys()) {
if (subStrMap.get(key) !== wordsMap.get(key)) {
isValid = false
break
}
}
}
if (isValid) { ans.push(left) }
left ++
right ++
}
return ans
};
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
wordlen = len(words[0])
substringlen = len(words) * wordlen
hashmap1 = Counter(words)
ans = []
for i in range(len(s) - substringlen + 1):
curwords = []
for j in range(i, i + substringlen, wordlen):
curwords.append(s[j:j + wordlen])
hashmap2 = Counter(curwords)
if hashmap1 == hashmap2:
ans.append(i)
return ans
Time complexity O(n*k) where k is the length of the substring Space complexity O(m) where m is the number of words
把words中的单词全部取出 放到哈希表中
再从s中取出和word中全部单词拼接长度相同的子串
从子串中截取单个单词长度的字符串 放去另外一个哈希表
比较两个哈希表
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
//输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
//输出:[6,9,12]
List<Integer> ans = new ArrayList<>();
if (s == null || s.length() == 0 || words == null || words.length == 0) {
return ans;
}
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 substring = s.substring(i, i + all_len);
HashMap<String, Integer> map1 = new HashMap<>();
for (int j = 0; j < all_len; j += one_word) {
String s1 = substring.substring(j, j + one_word);
//剪枝 加快速度
if(!map.containsKey(s1)){
break;
}
map1.put(s1, map1.getOrDefault(s1, 0) + 1);
}
if (map.equals(map1)) {
ans.add(i);
}
}
return ans;
}
}
复杂度分析 时间复杂度: O((n-m)*m) n为s中长度 m为word中全部字符串拼接后的长度 空间复杂度:O(n)为哈希表所需要的大小
用一个滑动窗口,窗口宽度是 words 列表里所有单词连接在一起的总长度。然后,遍历滑动窗口的所有位置,检查子串能不能按照 words 给定的列表进行分割。如果可以分割,就找到了一个起始位置。
滑动窗口总共有 n - m c + 1 个,n 是 s 的长度,m 是 words 里单词的长度, c 是words 的单词个数。 每次创建哈希来统计字母频率,复杂度是 O(mc) 每次尝试分割子串,复杂度是 O(c)
总时间复杂度是O((n - m c + 1) m c),空间复杂度是 O(mc )
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
m = len(words[0])
c =len(words)
n = len(s)
ans = []
w = Counter(words)
for i in range(n - m * c + 1):
d = Counter()
for j in range(c):
word = s[i + j * m: i + j * m + m]
if word not in w:
break
d[word] += 1
if d[word] > w[word]:
break
else:
ans.append(i)
return ans
class Solution {
public List
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
#先把给出的word按不同的组合排列 放到一个新的set中
#把每一个结果和s对比 如果符合就append 再标注首字母index 不符合就continue
# 投机取巧的办法 面试的时候可以这么解吗
import itertools
per = itertools.permutations(words)
words_set = set()
for x in per:
#words_set.add(x)
words_set.add(''.join(x))
res = []
l_sub = len(words[0])*len(words)
for i in range(len(s) - l_sub + 1):
substring = s[i:i+l_sub]
if substring in words_set:
res.append(i)
return res
①哈希表+遍历
由于words里的word有不同的组合顺序,考虑用哈希表保存,然后对s进行遍历,判断[i, i + word.length * words.length)里是否与哈希表中的匹配,可以将该子串按word.length分割,然后存进另一个哈希表中,比较两个哈希表是否相同。
② 哈希表+滑动窗口
由于每次遍历的时候都要把第二个哈希表清空,这样浪费了时间,因为从0开始的和从word.length的开始的第二个哈希表可能会存在公用的一部分。因此考虑[0, word.length)不同的起点,滑动窗口。
①
var findSubstring = function (s, words) {
let hash1 = new Map();
let res = [];
for (const word of words) {
hash1.set(word, (hash1.get(word) || 0) + 1);
};
const len = s.length;
const wLen = words[0].length;
const wsLen = words.length;
for (let i = 0; i < len - wLen * wsLen + 1; i++) {
let cur = s.substring(i, i + wLen * wsLen);
let hash2 = new Map();
var j = 0;
for (; j < cur.length; j += wLen) {
let string = cur.substring(j, j + wLen);
hash2.set(string, (hash2.get(string) || 0) + 1);
if (!hash1.has(string) || hash1.get(string) < hash2.get(string)) break;
};
if (j === cur.length) res.push(i);
};
return res;
};
②
var findSubstring = function (s, words) {
let hash1 = new Map();
let res = [];
for (const word of words) {
hash1.set(word, (hash1.get(word) || 0) + 1);
};
let n = s.length;
let word_len = words[0].length;
let words_len = words.length;
for(let i = 0; i < word_len; i++){
let left = i, right = i;
let count = 0;
let hash2 = new Map();
while(right + word_len <= n){
let newWord = s.substring(right, right + word_len);
right += word_len;
if(hash1.has(newWord)){
hash2.set(newWord, (hash2.get(newWord) || 0) + 1);
count++;
while(hash2.get(newWord) > hash1.get(newWord)){
let oldWord = s.substring(left, left + word_len);
hash2.set(oldWord, hash2.get(oldWord) - 1);
left += word_len;
count--;
}
}else{
left = right;
hash2.clear();
count = 0;
};
if(count === words_len) res.push(left);
}
};
return res;
};
复杂度分析
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
def strCount(s, n):
count = {}
for i in range(0, len(s), n):
count[s[i:i+n]] = count.get(s[i:i+n], 0)+1
return count
l = len(words)
lWord = len(words[0])
l *=lWord
count=collections.Counter(words)
i = 0
ans = []
curCount = {}
while i + l<=len(s):
if strCount(s[i:i+l], lWord) == count:
ans.append(i)
i += 1
return ans
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
/**
* @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;
}
from collections import Counter, defaultdict
class Solution(object):
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
cnt_words = Counter(words)
d = len(words[0])
l = d * len(words)
n = len(s)
ans = []
for i in range(0, n - l + 1, d):
for start in range(i, i + d):
if start + l <= n:
flag = True
windows = s[start:start + l]
cnt_s = defaultdict(int)
for j in range(0, l - d + 1, d):
ch = windows[j:j + d]
if ch in cnt_words:
cnt_s[ch] += 1
if cnt_s[ch] > cnt_words[ch]:
flag = False
break
else:
flag = False
break
if flag:
ans.append(start)
return ans
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
wh = len(words[0])
ans = []
# corner case
if len(s) < wh:
return ans
#
l = 0
# iterate the s
if len(s) == wh:
if len(words) != 1:
return []
if words[0] not in s:
return []
else:
return [0]
for i in range(wh, len(s)):
if s[l:i] in words:
allw = words.copy()
wl, wr = l, i
for wr in range(i, len(s)+1, wh):
if s[wl: wr] in allw:
allw.remove(s[wl: wr])
else:
break
#
if not allw:
ans.append(l)
break
wl += wh
l += 1
return ans
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) {
unordered_map<string, int> allWordsMap;
for (auto& v : words) {
++allWordsMap[v];
}
int num = words.size();
int onelen = words[0].length();
vector<int> res;
if (s.length() < num * onelen) {
return res;
}
for (int left = 0; left < s.length() - num * onelen + 1; ++left) {
unordered_map<string, int> nowWordsMap;
int right = left;
while (right < left + num * onelen) {
auto cur = s.substr(right, onelen);
if (allWordsMap.find(cur) == allWordsMap.end()
|| nowWordsMap[cur] == allWordsMap[cur]) {
break;
}
++nowWordsMap[cur];
right += onelen;
}
if (right == left + num * onelen) {
res.emplace_back(left);
}
}
return res;
}
};
滑动窗口
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;
}
Map<String, Integer> wordCounter = new HashMap<>();
for (String word : words) {
wordCounter.put(word, wordCounter.getOrDefault(word, 0) + 1);
}
int wordLength = words[0].length(), lengthOfs = s.length(), wordCount = words.length;
int wordsLength = wordLength * wordCount;
for (int i = 0; i < wordLength; i++) {
int left = i, right = i + wordLength, cnt = 0;
Map<String, Integer> currCount = new HashMap<>();
while (right <= lengthOfs) {
// 统计字符串 s 的字串中含有 words 字符串的数量
String currWord = s.substring(right - wordLength, right);
currCount.put(currWord, currCount.getOrDefault(currWord, 0) + 1);
cnt++;
right += wordLength;
while (currCount.getOrDefault(currWord, 0) > wordCounter.getOrDefault(currWord, 0)) {
String deleteWord = s.substring(left, left + wordLength);
currCount.put(deleteWord, currCount.getOrDefault(deleteWord, 0) - 1);
left += wordLength;
cnt--;
}
if (cnt == wordCount) {
res.add(left);
}
}
}
return res;
}
}
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
from collections import Counter
if not s or not words:return []
one_word = len(words[0])
all_len = len(words) * one_word
n = len(s)
words = Counter(words)
res = []
for i in range(0, n - all_len + 1):
tmp = s[i:i+all_len]
c_tmp = []
for j in range(0, all_len, one_word):
c_tmp.append(tmp[j:j+one_word])
if Counter(c_tmp) == words:
res.append(i)
return res
https://leetcode.com/problems/substring-with-concatenation-of-all-words/
word length * word count
(the sliding window width is the substring length).word length
.
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> result = new ArrayList<>();
Map<String, Integer> wordsMap = new HashMap<>();
for(String word: words){
wordsMap.put(word, wordsMap.getOrDefault(word, 0) + 1);
}
int wordLen = words[0].length();
int wordsNum = words.length;
for(int i = 0; i < s.length() - wordLen * wordsNum + 1; i++){
String curr = s.substring(i, i + wordLen * wordsNum);
Map<String, Integer> currMap = new HashMap<>();
int j = 0;
for(; j < curr.length(); j += wordLen){
String slice = curr.substring(j, j + wordLen);
if(!wordsMap.containsKey(slice)){
break;
}
currMap.put(slice, currMap.getOrDefault(slice, 0) + 1);
if(currMap.get(slice) > wordsMap.get(slice)){
break;
}
}
if(j == curr.length()){
result.add(i);
}
}
return result;
}
}
var findSubstring = function(s, words) {
const len = words[0].length
const res = []
// 循环, 循环的次数为字符串长度减去words所有字符串总和长度-1, 因为这个长度后面的字符串肯定不能拼接成words数组的数据
for (let i = 0; i <= s.length - words.length * len; i++) {
const wordsCopy = [...words]
// 深度优先遍历
dfs(wordsCopy, s.substring(i), i)
}
return res
function dfs(arr, s, start) {
// 递归的结束条件为数组的长度为0, 或者进不去下方的判断
if (arr.length === 0) return res.push(start)
// 从字符串开始剪切固定长度字符串, 去words中查找, 如果找不到, 结束, 如果找到了 继续往下查找
const str = s.substr(0, len)
const index = arr.findIndex((item) => item === str)
if (index > -1) {
// 递归查找之前需要将已经使用过的数组索引删除, 字符串也需要删除已经判断过的
arr.splice(index, 1)
dfs(arr, s.substring(len), start)
}
}
}
学习一下他人的代码
public List
var findSubstring = function(s, words) {
const len = words[0].length
const res = []
// 循环, 循环的次数为字符串长度减去words所有字符串总和长度-1, 因为这个长度后面的字符串肯定不能拼接成words数组的数据
for (let i = 0; i <= s.length - words.length * len; i++) {
const wordsCopy = [...words]
// 深度优先遍历
dfs(wordsCopy, s.substring(i), i)
}
return res
function dfs(arr, s, start) {
// 递归的结束条件为数组的长度为0, 或者进不去下方的判断
if (arr.length === 0) return res.push(start)
// 从字符串开始剪切固定长度字符串, 去words中查找, 如果找不到, 结束, 如果找到了 继续往下查找
const str = s.substr(0, len)
const index = arr.findIndex((item) => item === str)
if (index > -1) {
// 递归查找之前需要将已经使用过的数组索引删除, 字符串也需要删除已经判断过的
arr.splice(index, 1)
dfs(arr, s.substring(len), start)
}
}
};
时间很复杂 空间也很大 刚好通过了
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> re;
//两个map相等便可以。
// 1.先构建expected map
map<string,int> expect_map;
for(auto const & word:words){
expect_map[word]++;
}
int word_length = words[0].length();
int start =0 ; int end =word_length*words.size();
while (end<=s.size()){
if(is_equal(s,start,end,word_length,expect_map))re.push_back(start);
end++;
start++;
}
return re;
}
// 2. 判断是否相等
bool is_equal(string &s,int start,int end,int word_length,map<string,int> ex_map){
int pos =start;
while(pos!=end){
string key = s.substr(pos,word_length);
if(ex_map.find(key)==ex_map.end())return false;
ex_map[key]--;
if(ex_map.at(key)==0)ex_map.erase(key);
pos+=word_length;
}
return ex_map.empty();
}
};
// 1-3
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;
}
};
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/
使用哈希表记录 words 里面元素的个数,判断第一个字母开始的窗口是否存在于words,匹配则向后移动,重复操作。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> ans={};
int len=words[0].size();
if(len>s.size()) return ans;
unordered_map<string,int> mymap;
for(int i=0;i<words.size();i++)
mymap[words[i]]++;
for(int i=0;i<=s.size()-len;i++){
if(mymap.find(s.substr(i,len))==mymap.end()) continue;
unordered_map<string,int> tmp=mymap;
int j=i;
for(int count=words.size();count>0;count--){
string a=s.substr(j,len);
if(tmp.find(a)==tmp.end() || tmp[a]==0) break;
else{
j+=len;
tmp[a]--;
}
if(j==i+len*words.size()) ans.push_back(i);
}
}
return ans;
}
};
复杂度分析
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;
}
}
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
两个HashMap来处理。
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();
//HashMap1 存所有单词
HashMap<String, Integer> allWords = new HashMap<String, Integer>();
for (String w : words) {
int value = allWords.getOrDefault(w, 0);
allWords.put(w, value + 1);
}
//遍历所有子串
for (int i = 0; i < s.length() - wordNum * wordLen + 1; i++) {
//HashMap2 存当前扫描的字符串含有的单词
HashMap<String, Integer> hasWords = new HashMap<String, Integer>();
int num = 0;
//判断该子串是否符合
while (num < wordNum) {
String word = s.substring(i + num * wordLen, i + (num + 1) * wordLen);
//判断该单词在 HashMap1 中
if (allWords.containsKey(word)) {
int value = hasWords.getOrDefault(word, 0);
hasWords.put(word, value + 1);
//判断当前单词的 value 和 HashMap1 中该单词的 value
if (hasWords.get(word) > allWords.get(word)) {
break;
}
} else {
break;
}
num++;
}
//判断是不是所有的单词都符合条件
if (num == wordNum) {
res.add(i);
}
}
return res;
}
时间复杂度: O(N*m) m为word数组 空间复杂度:O(m)
class Solution(object): def findSubstring(self, s, words): """ :type s: str :type words: List[str] :rtype: 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): """ :type s: str :type words: List[str] :rtype: 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
++
难度困难585
给定一个字符串 s
和一些 长度相同 的单词 words
。找出 s
中恰好可以由 words
中所有单词串联形成的子串的起始位置。
注意子串要与 words
中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words
中单词串联的顺序。
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
if not s or not words:
return []
wordmap = {}
smap = {}
for word in words:
wordmap[word] = wordmap.get(word, 0) + 1
wordlen = len(words[0])
wordnum = len(words)
res = []
for k in range(wordlen):
i = k
j = k
# 以s[i]开头的子串是否符合?
while i <= len(s) - wordlen * wordnum:
# 获取长度为wordlen * wordnum的子串
while(j < i + wordlen * wordnum):
# 当前长度为wordlen的子串
tmp = s[j: j + wordlen]
smap[tmp] = smap.get(tmp, 0) + 1
j += wordlen
# 获取失败:刚获取的tmp在wordmap不存在,重置i,restart
if wordmap.get(tmp, 0) == 0:
i = j
smap.clear()
break
elif smap[tmp] > wordmap[tmp]:
# 获取失败,tmp 的数量超出wordmap中的数量,则需要不停地删除,直到数值不在高于wordmap中的数值
# 并重置i,restart
while smap[tmp] > wordmap[tmp]:
smap[s[i: i + wordlen]] -= 1
i += wordlen
break
# 至此,已经获取长度为wordlen * wordnum的子串,或长度不足
if j == i + wordlen * wordnum:
res.append(i) # 将当前i添加到res
# 删除第一个长度为wordlen的子串, 并开始下一个i
smap[s[i: i + wordlen]] -= 1
i += wordlen #
smap.clear()
return res
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if(words.empty()) return {};
vector<int> ans;
unordered_map<string, int> wordmap, smap;
for(string word: words){
wordmap[word]++;
}
int wordnum = words.size();
int wordlen = words[0].size();
for(int k = 0; k < wordlen; k++){
int i = k, j = k;
while(i < (s.size() - wordlen * wordnum + 1)){
while(j < i + wordlen * wordnum){
string tmp = s.substr(j, wordlen);
smap[tmp]++;
j += wordlen;
// 不存在wordmap
if(wordmap[tmp] == 0){
i = j;
smap.clear();
break;
}
else if(smap[tmp] > wordmap[tmp]){
while(smap[tmp] > wordmap[tmp]){
smap[s.substr(i, wordlen)]--;
i += wordlen;
}
break;
}
}
if(j == i + wordlen * wordnum){
ans.push_back(i);
smap[s.substr(i, wordlen)]--;
i += wordlen;
}
}
smap.clear();
}
return ans;
}
};
外层遍历字符串,内层遍历从当前位置到总单词长度的位置,匹配该字符串是否符合
Java Code:
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<Integer>();
if(s.length()<=0||words.length<=0) {
return res;
}
//定义一个map,存单词和对应的次数
Map<String,Integer> words_map = new HashMap<String,Integer>();
for(String str : words) {
if(words_map.containsKey(str)) {
words_map.put(str, words_map.get(str)+1);
}else {
words_map.put(str, 1);
}
}
int wordLen=words[0].length();
int wordStrLen=wordLen*words.length; //字符串总体长度
for(int i=0;i<s.length()-wordStrLen+1;i++) {
String tmpStr=s.substring(i,i+wordStrLen);
Map<String,Integer> wordsTmpMap = new HashMap<String,Integer>(words_map);
for(int j=0;j<wordStrLen;j+=wordLen) {
String word=tmpStr.substring(j,j+wordLen);
if(wordsTmpMap.containsKey(word)){
wordsTmpMap.put(word, wordsTmpMap.get(word)-1);
if(wordsTmpMap.get(word)==0) {
wordsTmpMap.remove(word);
}
}else {
break;
}
}
if(wordsTmpMap.size()==0){
res.add(i);
}
}
return res;
}
}
复杂度分析
令 n 为数组长度。
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 { public List<Integer> findSubstring(String s, String[] words) { List<Integer> list = new ArrayList<>(); if(words==null||words.length==0)return list; int size= words[0].length(); HashMap<String,Integer> map=new HashMap<>(); for (String temp:words){ map.put(temp,map.getOrDefault(temp,0)+1); } for(int i=0;i+size<=s.length();i+=1){ if(!map.containsKey(s.substring(i,i+size)))continue; int j=i; HashMap<String,Integer> map_t=new HashMap<>(map); while (j+size<=s.length()&&!map_t.isEmpty()){ String temp=s.substring(j,j+size); if(!map_t.containsKey(temp)){ break; }else{ int t=map_t.get(temp); if(t==1){ map_t.remove(temp); }else{ map_t.put(temp,t-1); } } j+=size; } if(map_t.isEmpty()){ list.add(i); } } return list; } }
时间复杂度:O(n*n)
空间复杂度:O(n)
用hash先记录每个单词出现次数,再遍历字符串,每次搜索单词,由于所有单词相同长度,因此可以substr截取相同长度字符串,得到单词。同时已知单词个数,那么就比对各个窗口的单词是否在hashtable中存在,若无,则整体向后移动,若有则检查下一个窗口,直到全部检查结束,将起始index进行记录
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> ans;
unordered_map<string,int> helper;
int wordsize=words[0].size();
int totalsize=wordsize*words.size();
if(wordsize>s.size()) return ans;
for(auto it:words){
helper[it]++;
}
for(int i=0;i<=s.size()-wordsize;i++){
if(helper.find(s.substr(i,wordsize))!=helper.end()){
unordered_map<string,int> temp=helper;
int strat=i;
// words.size()个窗口
for(int j=0;j<words.size();j++){
string str=s.substr(strat,wordsize);
if(temp.find(str)==temp.end()||temp[str]==0) break;
strat+=wordsize;//查看下一个窗口
temp[str]--;
}
if(strat==i+totalsize){
ans.push_back(i);
}
}
}
return ans;
}
};
思路 从 words 入手,words 所有单词排列生成字符串 X, 通过字符串匹配查看 X 在 s 中的出现位置
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;
}
}
时间复杂度 O(n∗m∗k)。
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; };
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
if (words == null) return null;
Map<String,Integer> need = new HashMap<>();
for (String word:words) need.put(word,need.get(word)==null?1:need.get(word)+1);
int len = words[0].length();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < len; i++) {
Map<String,Integer> window = new HashMap<>();
int left=i,right=i,count=0;
while (right <= s.length() - len) {
String str = s.substring(right,right + len);
right+=len;
if (need.containsKey(str)) {
window.put(str,window.get(str)==null?1:window.get(str)+1);
if (need.get(str).equals(window.get(str))) count++;
}
// 如果窗口中的字符长度恰好等于所需要的字符长度 则判断是否有满足单词全匹配
if ((right - left) / len == words.length) {
if (count == need.size()) list.add(left);
String str1 = s.substring(left,left + len);
left+=len;
if (need.containsKey(str1)) {
if (need.get(str1).equals(window.get(str1))) count--;
// 删除窗口中的字符串
window.put(str1,window.get(str1)-1);
}
}
}
}
return list;
}
}
JavaScript Code:
var findSubstring = function (s, words) {
const len = words[0].length
const res = []
for (let i = 0; i <= s.length - words.length * len; i++) {
const wordsCopy = [...words]
dfs(wordsCopy, s.substring(i), i)
}
return res
function dfs(arr, s, start) {
if (arr.length === 0) return res.push(start)
const str = s.substr(0, len)
const index = arr.findIndex((item) => item === str)
if (index > -1) {
arr.splice(index, 1)
console.log(arr.splice(index, 1))
dfs(arr, s.substring(len), start)
}
}
}
用了一种滑动窗口的方法
因为单词都是定长的,所以本质上和单个字符一样。
比如s = "a1b2c3a1d4",words=["a1", "b2", "c3", "d4"]
窗口最开始为空:
a1在集合中,加入窗口 【a1】b2c3a1d4
b2在集合中,加入窗口 【a1b2】c3a1d4
c3在集合中,加入窗口 【a1b2c3】a1d4
a1在集合中,但前面a1已经出现了一次,达到了次数上限,不能再加入窗口。此时只需要把窗口向右移动一个单词:a1【b2c3a1】d4
d4在集合中,加入窗口a1【b2c3a1d4】此时找到了一个匹配。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int word_len = words[0].size();
int len = words.size()*word_len;
int n = s.size();
unordered_map<string, int> m; // <单词,出现的次数>
for(int i=0; i<words.size(); ++i)
{
if(m.count(words[i]) == 0) // 注意count方法的使用
m.insert(make_pair(words[i], 1));
else
++m[words[i]];
}
vector<int> index;
for(int i=0; i<word_len; ++i)
{
unordered_map<string, int> win;
int left = i; // 窗口左边沿
int count = 0; // 窗口中的单词数目
for(int right=i; right<=n-word_len; right+=word_len)
{
string word = s.substr(right, word_len);
if(m.find(word) != m.end()) // 在集合中
{
if(win.find(word) == win.end()) // 不在窗口中,即添加
win[word] = 1;
else
++win[word]; // 在窗口中
if(win[word] <= m[word]) // 还没出现相应的次数
++count;
else
{
// 单词在集合中,但是它已经在窗口中出现了相应的次数,不应该加入窗口
// 此时把窗口起始位置想左移动到,该单词第一次出现的位置的下一个单词位置
for(int k=left; ; k+=word_len)
{
string temp = s.substr(k, word_len);
--win[temp];
if(temp == word)
{
left = k + word_len;
break;
}
--count;
}
}
if(count == words.size())
index.push_back(left);
}
else // 不在集合中,从窗口右侧重新开始
{
left = right + word_len;
win.clear();
count = 0;
}
}
}
return index;
}
};
复杂度分析
Code:
public class Solution {
public IList
if (words == null || words.Length == 0)
return result;
Dictionary<string, int> strDict = new Dictionary<string, int>();
foreach(string word in words)
{
if (!strDict.ContainsKey(word))
strDict.Add(word, 0);
strDict[word]++;
}
int strLen = s.Length;
int wordLen = words[0].Length;
int count = words.Length;
int wordsLen = wordLen * count;
if (strLen < wordsLen)
return result;
for ( int i = 0; i < strLen - wordsLen + 1; i++)
{
string currentStr = s.Substring(i, wordsLen);
Dictionary<string, int> currentDict = new Dictionary<string, int>();
int currentIndex = 0;
while ( currentIndex < currentStr.Length)
{
string currentWord = currentStr.Substring(currentIndex, wordLen);
if (!strDict.ContainsKey(currentWord))
break;
if (!currentDict.ContainsKey(currentWord))
currentDict.Add(currentWord, 0);
currentDict[currentWord]++;
if (currentDict[currentWord] > strDict[currentWord])
break;
currentIndex += wordLen;
}
if (currentIndex == currentStr.Length)
result.Add(i);
}
return result;
}
}
C++ Code:
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
unordered_map<string, int> allWordsMap;
for (auto& v : words) {
++allWordsMap[v];
}
int num = words.size();
int onelen = words[0].length();
vector<int> res;
if (s.length() < num * onelen) {
return res;
}
for (int left = 0; left < s.length() - num * onelen + 1; ++left) {
unordered_map<string, int> nowWordsMap;
int right = left;
while (right < left + num * onelen) {
auto cur = s.substr(right, onelen);
if (allWordsMap.find(cur) == allWordsMap.end()
|| nowWordsMap[cur] == allWordsMap[cur]) {
break;
}
++nowWordsMap[cur];
right += onelen;
}
if (right == left + num * onelen) {
res.emplace_back(left);
}
}
return res;
}
};
复杂度分析
vector<int> findSubstring(string s, vector<string>& words) {
if(words.empty()) return {};
unordered_map<string,int> wordmap,smap;
for(string word:words) wordmap[word]++;
int wordlen = words[0].size();
int wordnum = words.size();
vector<int> ans;
for(int k=0;k<wordlen;k++){
int i=k,j=k;
while(i<s.size()-wordnum*wordlen+1){
while(j<i+wordnum*wordlen){
string temp = s.substr(j,wordlen);
smap[temp]++;
j+=wordlen;
if(wordmap[temp]==0){//情况二,有words中不存在的单词
i=j;//对i加速
smap.clear();
break;
}
else if(smap[temp]>wordmap[temp]){//情况三,子串中temp数量超了
while(smap[temp]>wordmap[temp]){
smap[s.substr(i,wordlen)]--;
i+=wordlen;//对i加速
}
break;
}
}
//正确匹配,由于情况二和三都对i加速了,不可能满足此条件
if(j==i+wordlen*wordnum){
ans.push_back(i);
smap[s.substr(i,wordlen)]--;
i+=wordlen;//i正常前进
}
}
smap.clear();
}
return ans;
}
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;
}
}
每当匹配到一个数组内的字符串的时候,就检验后续的字符串是否满足要求,满足则记录第一个位置,不满足则继续
var findSubstring = function(s, words) {
if(!s || !words.length) return [];
let singleLength = words[0].length;
let totalLength = singleLength * words.length;
let arr = [];
let obj = {};
words.forEach(vv => {
obj[vv] = (obj[vv] || 0) + 1;
})
for(let i = 0; i < s.length; i++) {
let str = s.slice(i,i + singleLength);
if(obj[str]) {
let temp = Object.assign({},obj);
for(let j = i; j < i + totalLength;j = j + singleLength) {
let tempStr = s.slice(j,j+ singleLength);
if(temp[tempStr]) {
temp[tempStr] = temp[tempStr] - 1;
} else {
break;
}
}
if(Object.values(temp).every(vv=>vv===0)) {
arr.push(i);
}
}
}
return arr;
};
时间复杂度:O(n)
空间复杂度:O(n)
https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words
思路:
从s串入手,遍历所有长度为word[o].length *words.length的子串,查看y是否可以由words构成
第一个哈希表存储words中单词出现的频数
第二个哈希表存储S字符串中,遍历出现的单词的频数
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;
}
}
复杂度分析:
时间复杂度:O(nmk) 字串长度为mk,对于每段检查n-mK次
空间复杂度:O(M)
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 {
public:
vector<int> findSubstring(string s, vector<string>& words) {
int n = s.size(), m = words.size(), w = words[0].size();
vector<int> res;
unordered_map<string, int> tot;
for (auto& word : words) tot[word]++;
if (words.empty()) return res;
for (int i = 0; i < w; i++) {
int cnt = 0;
unordered_map<string, int> wd;
for (int j = i; j <= 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;
}
};
与官方题解相似,遍历 s 串中所有长度为 (words[0].length * words.length) 的子串 W,查看 W是否可以由 words 数组构造生成,用两个map做判断
/**
* @param {string} s
* @param {string[]} words
* @return {number[]}
*/
var findSubstring = function(s, words) {
const all_len = words[0].length * words.length
const w_len = words.length
const ws = words[0].length
const num_map = new Map()
const res = []
words.forEach(v => {
num_map.set(v, (num_map.get(v) || 0) + 1)
})
const map = new Map()
for (let i = 0; i < s.length - all_len + 1; i++) {
const str = s.substr(i, all_len)
let step = 0
let num = 0
map.clear()
while (num < w_len) {
const ss = str.substr(step, ws)
if (!num_map.has(ss) || ((map.get(ss) || 0) >= num_map.get(ss))) {
break
} else {
map.set(ss, (map.get(ss) || 0) + 1)
num += 1
step += ws
}
}
if (num === w_len) {
res.push(i)
}
}
return res
};
令 n 为字符串 S 长度, m 为 words 数组元素个数, k 为单个 word 字串长度。
时间复杂度: 本质上我们的算法是将 s 划分为若干了段,这些段的长度为 m km∗k,对于每一段我们最多需要检查 n - m kn−m∗k 次,因此时间复杂度为 O(n m k)O(n∗m∗k)。
空间复杂度: temp 在下一次循环会覆盖上一次的 temp,因此 temp 的空间在任意时刻都不大于 O(m)O(m), 因此空间复杂度为 O(m)O(m)。
多起点滑动窗口+哈希表+哈希表
class Solution:
def findSubstring(self, s: str, words: List[str]) -> List[int]:
num_word = len(words)
word_len = len(words[0])
total_word_len = num_word * word_len
cnt = Counter(words)
res = []
n = len(s)
for i in range(word_len): # world_len 个滑动窗口
left = right = i # 窗口的左右边界
cur_cnt = Counter() # 当前窗口中的word哈希表
cur_num_word = 0
while right + word_len <= n:
word = s[right:right+word_len] # 窗口中新出现的词 word
right += word_len # 窗口继续往右滑动
# 【保证新出现的word在words中】间接保证当前窗口中的word均在words里,只是出现次数待计算
# if word not in cnt:
if cnt[word] == 0: # 窗口中新出现的word未在words中,此时直接将left移动到right处,当前窗口大小变为0,不包含任何词
left = right
cur_cnt.clear() # 清空当前窗口统计量【当前窗口大小为0】
cur_num_word = 0
continue
# 否则,若word存在与words里,则统计word信息,并根据情况移动left
cur_cnt[word] += 1 # word的统计量+1
cur_num_word += 1
# 新出现的word导致当前窗口中word的统计量超限,left需往右滑动,直至统计量达标
while cur_cnt[word] > cnt[word]:
left_word = s[left: left+word_len]
cur_cnt[left_word] -= 1
cur_num_word -= 1
left += word_len
if cur_num_word == num_word:
res.append(left)
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 {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> ans = new ArrayList<>();
if (words.length == 0) return ans;
int n = s.length(), m = words.length, w = words[0].length();
// 统计 words 中「每个目标单词」的出现次数
Map<String, Integer> map = new HashMap<>();
for (String word : words) {
map.put(word, map.getOrDefault(word, 0) + 1);
}
for (int i = 0; i < w; i++) {
// 构建一个当前子串对应 map,统计当前子串中「每个目标单词」的出现次数
Map<String, Integer> curMap = new HashMap<>();
// 滑动窗口的大小固定是 m * w
// 每次将下一个单词添加进 cur,上一个单词移出 cur
for (int j = i; j + w <= n; j += w) {
String cur = s.substring(j, j + w);
if (j >= i + (m * w)) {
int idx = j - m * w;
String prev = s.substring(idx, idx + w);
if (curMap.get(prev) == 1) {
curMap.remove(prev);
} else {
curMap.put(prev, curMap.get(prev) - 1);
}
}
curMap.put(cur, curMap.getOrDefault(cur, 0) + 1);
// 如果当前子串对应 map 和 words 中对应的 map 相同,说明当前子串包含了「所有的目标单词」,将起始下标假如结果集
if (map.containsKey(cur) && curMap.get(cur).equals(map.get(cur)) && cmp(map, curMap)) {
ans.add(j - (m - 1) * w);
}
}
}
return ans;
}
// 比较两个 map 是否相同
boolean cmp(Map<String, Integer> m1, Map<String, Integer> m2) {
if (m1.size() != m2.size()) return false;
for (String k1 : m1.keySet()) {
if (!m2.containsKey(k1) || !m1.get(k1).equals(m2.get(k1))) return false;
}
for (String k2 : m2.keySet()) {
if (!m1.containsKey(k2) || !m1.get(k2).equals(m2.get(k2))) return false;
}
return true;
}
}
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"] 输出:[]