leetcode-pp / 91alg-8-daily-check

5 stars 0 forks source link

【Day 23 】2022-08-06 - 30. 串联所有单词的子串 #38

Closed azl397985856 closed 2 years ago

azl397985856 commented 2 years ago

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"] 输出:[]

djd28176 commented 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;
    }   
}
laofuWF commented 2 years ago
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
yopming commented 2 years ago

先把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;
    }
};
MichaelXi3 commented 2 years ago

Idea

graph LR
A[Sliding Window] -->|滚动|B(HashMap)

Code

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;
    }
}
flaming-cl commented 2 years ago
// 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;
};
joeymoso commented 2 years ago
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
dereklisdr commented 2 years ago

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;

}

}

richypang commented 2 years ago

代码

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)
xixiao51 commented 2 years ago

Idea

Sliding window

Code

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

ashleyyma6 commented 2 years ago

Idea

Code


 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

zzzkaiNS commented 2 years ago

思路

字符串哈希

代码

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;
    }
};
lbc546 commented 2 years ago

Sliding window and compare Counter

Python solution

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

Complexity

O(n)

thinkfurther commented 2 years ago
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)

Cusanity commented 2 years ago

Solution

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);
}
caterpillar-0 commented 2 years ago

思路

哈希表数组,将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;
    }
};

复杂度分析

ILoveQier commented 2 years ago
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;
};
kiirii4 commented 2 years ago

思路

滑动窗口 + 哈希表

代码

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;
    }
};

复杂度分析

nikojxie commented 2 years ago

思路

  1. 先定义一个map,遍历words存储每个单词出现的次数;
  2. 假设单个word长度为n,从字符串开头开始,遍历长度为n*words.length的子串,从该子串头开始每次取n个字符(假设为key)判断是否存在在map内,若存在,则map[key]--,若不存在或小于等于0,退出遍历,说明该子串不符合条件;
  3. 若遍历到最后,发先都是符合条件的,则该子串满足,记录其首位下标;
  4. 往后推一位,继续步骤2。

代码

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
};
testplm commented 2 years ago
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
LiquanLuo commented 2 years ago
/****
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;
            }
        }

    }

};
zch-bit commented 2 years ago

Day 23

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
}
luckyTWJ commented 2 years ago

解题思路

滑动窗口 用一个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;
    }
}
haoyangxie commented 2 years ago

Idea

滑动窗口+哈希表

Code

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;
    }
}
YANGZ001 commented 2 years ago

LeetCode Link

Substring with Concatenation of All Words - LeetCode

Idea

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.

Code

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;
    }
}

Complexity Analysis

Time Complexity

O(N^2)

Space Complexity

O(N)

User-Vannnn commented 2 years ago

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
aiweng1981 commented 2 years ago

思路

  • 思路描述:确实好难、

代码

#代码
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)
lzyxts commented 2 years ago

Idea

Code

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

Complexity

Tlntin commented 2 years ago

题目链接

题目思路

#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;
}

复杂度

结果

ef460f5b1e95fdaa32ba0d5ad98ae7fa.png

优化思路

代码,优化后

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);
  }
};

优化后结果

cfd6a4f0041520cd84becbd6078509d0.png

andyyxw commented 2 years ago
/*
 * 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
};
ceramickitten commented 2 years ago

思路

滑动窗口扫描字符串,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

zzz607 commented 2 years ago

思路

先统计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. 时间复杂度:外层循环为O(n),内层循环为:O(len(words)*len(words[0]),合起来即为O(nm)
  2. 空间复杂度:O(n)
adamw-u commented 2 years ago

思路
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)

Whisht commented 2 years ago

思路

findalyzhou commented 2 years ago

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;
    }
}
yufeng727 commented 2 years ago

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; };

shiyishuoshuo commented 2 years ago

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;
    }
}
phoenixflyingsky commented 2 years ago

Idea

slide window

Code


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

AtaraxyAdong commented 2 years ago

思路

想不出来,暴力解法

代码

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;

    }
}
kernelSue commented 2 years ago
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;
    }
}
ZhongXiangXiang commented 2 years ago

思路

先用哈希表存储每个单词的数量;遍历一遍字符串,然后内部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) (分析应该不是很对)

xxiaomm commented 2 years ago
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;
    }
}
LuckyRyan-web commented 2 years ago

/**
 * @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;
}
uyplayer commented 2 years ago

LC 30. 串联所有单词的子串

Leetcode连接

暴力破解,但是这两个方法超时

Golang 代码


//
//  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
}

复杂度分析

哈希表+滑动窗口

双哈希表+滑动窗口

Golang 代码


//
//  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
}

复杂度分析

aouos commented 2 years ago
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;
};
luckyoneday commented 2 years ago

代码 js

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
};
hydelovegood commented 2 years ago

思路

划分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

Elon-Lau commented 2 years ago

思路

  • 思路描述

代码

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

复杂度

  • 时间复杂度:
  • 空间复杂度:
frankelzeng commented 2 years ago
 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
yibenxiao commented 2 years ago

【Day 23】30. 串联所有单词的子串

代码

// 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)

cytrue commented 2 years ago

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;
}

}