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

6 stars 0 forks source link

【Day 23 】2022-04-23 - 30. 串联所有单词的子串 #26

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

JiangWenqi commented 2 years ago
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        if (words.empty()) return res;
        int m = words.size(), w = words[0].size(), mw = m * w, cnt = 0;
        unordered_map<string, int> wf, wd;
        for (auto& word : words) wf[word]++;
        for (int i = 0; i < w; i++) {
            for (int j = i; j + w <= s.size(); j += w) {
                if (j >= i + mw) {
                    string word = s.substr(j - mw, w);
                    wd[word]--;
                    if (wd[word] < wf[word]) cnt--;
                }
                string word = s.substr(j, w);
                wd[word]++;
                if (wd[word] <= wf[word]) cnt++;
                if (cnt == m) res.push_back(j - (m - 1) * w);
            }
            wd.clear();
            cnt = 0;
        }

        return res;
    }
};

TC: O(N*Len) SC: O(N)

zqwwei commented 2 years ago

Code

    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        wds_count = len(words)
        wd_len = len(words[0])
        lenght = wds_count * wd_len
        wordCnt = defaultdict(int)

        for word in words:
            wordCnt[word] += 1

        def is_concatenation(i):
            # corner case: 可以重复的 
            count = wds_count
            wordFreq = dict(wordCnt)
            for x in range(wds_count):
                start = i + x * wd_len 
                wd = s[start: start + wd_len]
                if wd in wordFreq:
                    wordFreq[wd] -= 1
                    if wordFreq[wd] >= 0:
                        count -= 1
            return count == 0

        ptr = 0
        res = []
        while ptr <= len(s) - lenght:
            if is_concatenation(ptr):
                res.append(ptr)
            ptr += 1

        return res
currybeefer commented 2 years ago

思路:参考了题解

vector<int> findSubstring(string s, vector<string>& words) 
    {
        vector<int> res;
        int wordNum=words.size();
        int wordLen=words[0].size();
        unordered_map<string,int> allWords;

        for(string it:words)
            allWords[it]++;

        for(int i=0;i<s.size()-wordLen*wordNum+1;i++)
        {
            unordered_map<string,int> window;
            int num=0;
            while(num<wordNum)
            {
                string word=s.substr(i+num*wordLen,wordLen);
                if(allWords[word]==0) break;
                else
                {
                    window[word]++;
                    if(window[word]>allWords[word]) break;
                }
                num++;
            }
            if(num==wordNum) res.push_back(i);
        }

        return res;
    }

时间复杂度:O(n+len) 空间复杂度:O(n)

freedom0123 commented 2 years ago
#include <string>
#include <vector>
#include <unordered_map>

using namespace std;
class Solution {
public:
    unordered_map<string, int> map;
    int totalLen;
    int oneWordLen;
    int finalProgress;
    vector<int> res;
    vector<int> findSubstring(string s, vector<string>& words) {
        for(string& str : words)
            map[str]++;

        oneWordLen = words[0].size();
        totalLen   = oneWordLen * words.size();
        finalProgress = words.size();

        if(s.size() < totalLen)
            return res;

        //to try to constrcut the target string
        for(int i = 0; i <= s.size() - totalLen; i++){
            dfs(s, i, 0);
        }

        return res;
    }

    void dfs(string& s, int curPos, int progress){
        if(progress == finalProgress){
            res.push_back(curPos - totalLen);
            return;
        }

        string frac = s.substr(curPos, oneWordLen);

        if(map[frac] == 0)
            return;

        map[frac]--;
        dfs(s, curPos + oneWordLen, progress + 1);
        map[frac]++;

    }
};
ZacheryCao commented 2 years ago

Code

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        n = len(s)
        k = len(words)
        word_length = len(words[0])
        substring_size = word_length * k
        word_count = collections.Counter(words)

        def sliding_window(left):
            words_found = collections.defaultdict(int)
            words_used = 0
            excess_word = False

            # Do the same iteration pattern as the previous approach - iterate
            # word_length at a time, and at each iteration we focus on one word
            for right in range(left, n, word_length):
                if right + word_length > n:
                    break

                sub = s[right : right + word_length]
                if sub not in word_count:
                    # Mismatched word - reset the window
                    words_found = collections.defaultdict(int)
                    words_used = 0
                    excess_word = False
                    left = right + word_length # Retry at the next index
                else:
                    # If we reached max window size or have an excess word
                    while right - left == substring_size or excess_word:
                        # Move the left bound over continously
                        leftmost_word = s[left : left + word_length]
                        left += word_length
                        words_found[leftmost_word] -= 1

                        if words_found[leftmost_word] == word_count[leftmost_word]:
                            # This word was the excess word
                            excess_word = False
                        else:
                            # Otherwise we actually needed it
                            words_used -= 1

                    # Keep track of how many times this word occurs in the window
                    words_found[sub] += 1
                    if words_found[sub] <= word_count[sub]:
                        words_used += 1
                    else:
                        # Found too many instances already
                        excess_word = True

                    if words_used == k and not excess_word:
                        # Found a valid substring
                        answer.append(left)

        answer = []
        for i in range(word_length):
            sliding_window(i)

        return answer
forestie9 commented 2 years ago

Ideas

Refer to solutions: use hashmap to record count of each word, and iterate every n length of word to update the seen dictionary.

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if len(s) == 0 or words == []:
            return []

        k = len(words)
        n = len(words[0])
        ans = []
        count = Counter(words)

        for i in range(len(s) - k * n + 1):
            seen = defaultdict(int)
            j = 0
            while j < k:
                word = s[i + j * n: i + j * n + n]
                seen[word] += 1
                if seen[word] > count[word]:
                    break
                j += 1
            if j == k:
                ans.append(i)

        return ans

O(a*n) time, O(a+b) space

houmk1212 commented 2 years ago

思路

用哈希表保存words中字符串的出现次数。然后暴力枚举的s中的每个位置,看能否构成满足题意的字符串。

代码

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        HashMap<String,Integer> map = new HashMap<>();
        int n = s.length();
        int sl = words[0].length();
        List<Integer> res = new ArrayList<>();

        if (n < sl){
            return new ArrayList<>();
        }

        for (String str : words) {
            map.put(str,map.getOrDefault(str,0)+1);
        }

        for (int i = 0; i < n; i++) {
            int x = i;
            if (i + sl * words.length > n)
                break;
            String str = s.substring(x,x+sl);
            HashMap<String,Integer> tmp = new HashMap<>(map);

            while (!tmp.isEmpty() && tmp.containsKey(str)) {
                x = x + sl;
                tmp.put(str,tmp.get(str) - 1);
                if (tmp.get(str) == 0){
                    tmp.remove(str);
                }
                if (x + sl > n) {
                    break;
                }
                str = s.substring(x,x+sl);
            }

            if (tmp.isEmpty()){
                res.add(i);
            }
        }
        return res;
    }
}

复杂度

Geek-LX commented 2 years ago

4.23

思路:滑动窗口法:一直在 s 维护着所有单词长度总和的一个长度队列!

代码:

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        from collections import Counter
        if not s or not words:return []
        one_word = len(words[0])
        word_num = len(words)
        n = len(s)
        words = Counter(words)
        res = []
        for i in range(0, one_word):
            cur_cnt = 0
            left = i
            right = i
            cur_Counter = Counter()
            while right + one_word <= n:
                w = s[right:right + one_word]
                right += one_word
                cur_Counter[w] += 1
                cur_cnt += 1
                while cur_Counter[w] > words[w]:
                    left_w = s[left:left+one_word]
                    left += one_word
                    cur_Counter[left_w] -= 1
                    cur_cnt -= 1
                if cur_cnt == word_num :
                    res.append(left)
        return res

时间复杂度:O(n)

caterpillar-0 commented 2 years ago

思路

滑动窗口,两个哈希表,一个记录words,一个记录滑动子串

代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        if(words.size()==0){
            return {};
        }
        int word_len=words[0].size();
        int word_num=words.size();
        //滑动窗口长度
        int win_len=word_len*word_num;
        //哈希表m1记录words
        unordered_map<string,int> m1;
        for(string num:words){
            m1[num]++;
        }
        for(int i=0;i<s.size()-win_len+1;i++){
            //哈希表m2记录滑动子串
            unordered_map<string,int> m2;
            int j;
            for(j=i;j<i+win_len;j+=word_len){
                //利用string截取子字符串substr
                string sub=s.substr(j,word_len);
                m2[sub]++;
                if(m1[sub]==0 || m1[sub]<m2[sub]){
                    break;
                }
            }
            if(j==i+win_len){
                res.push_back(i);
            }
        }
        return res;
    }
};

复杂度分析

hyh331 commented 2 years ago

Day23 思路

  1. 输入:s = "barfoothefoobarman", words = ["foo","bar"]
  2. 长度为n的字符串,大小为m的单词数组words,其中的所有单词长度相同为d。
  3. 维护两个哈希表,哈希表1:key(单词数组中出现的所有单词) value(单词出现的次数)
  4. 然后扫描字符串单词,若当前单词在哈希表1中,就将此单词存到哈希表2中,
  5. 哈希表2:key() value()

    class Solution {
    public:
    vector<int> findSubstring(string s, vector<string>& words) {
    vector<int> res;//?????????????????
    //字符串长度n,words中单词个数m,单词长度d
    int n=s.size(), m=words.size(), d=words[0].size();
    int len=m*d;//表示words数组所能拼接的字符串长度,即滑动窗口的长度。
    //哈希表1,存所有单词及其出现次数
    unordered_map<string,int> allwords;
    for(string w:words){
        allwords[w]++;//这里需要再看一下map原理,是怎么将单词加入到hashmap中的
    }
    //初始化0~d-1滑动窗口对应的词频统计表
    vector<unordered_map<string, int>> sm(d); 
    for(int i=0; i<d&&i+len<=n ; i++){
        //substr(j,d)复制子字符串,要求从指定位置j开始,并具有指定的长度d.并且每次增加单词长度d
        //j=i开始!!!
        for(int j = i ; j < i+len ; j+=d){
            string str=s.substr(j,d);
            sm[i][str]++;//???????????????
        }
        if(sm[i]==allwords){
            res.push_back(i);//????????????
        }
    }
    
        //移动滑动窗口,每次出一个单词,进一个单词
        for(int i=d; i+len<=n ; i++){
            int r=i%d;
            string str1=s.substr(i-d,d);//出窗字符串
            string str2=s.substr(i+len-d,d);//出窗字符串
            if(--sm[r][str1]==0)
                sm[r].erase(str1);
            sm[r][str2]++;
            if(sm[r]==allwords)
                res.push_back(i);
         }   
             return res;
    }
    };

    复杂度分析

    • 时间复杂度:O((d+m)*n),其中 n 表示字符串 s 的长度,d 是 words 中单词的长度, m 是 words 中单词的个数。
    • 空间复杂度:O(L),其中 LL表示words 数组中所有单词的长度之和
kite-fly6618 commented 2 years ago

思路:

hashmap + 暴力搜索

代码:

var findSubstring = function(s, words) {
    if (!s || !words || !words.length) return [];
    let wordLen = words[0].length // 单个字符的长度
    let len = words.length // 单词的个数
    let allWordsLen = wordLen*len
    let ans = [], wordMap = {};
    // 哈希表记录每个字符出现的次数
    for (let w of words) {
        wordMap[w] ? wordMap[w]++ :wordMap[w] = 1
    }
    for (let i = 0; i < s.length - allWordsLen + 1; i++) {
        let wm = Object.assign({}, wordMap);
        for (let j = i; j < i + allWordsLen - wordLen + 1; j += wordLen) {
            let w = s.slice(j, j + wordLen);
            if (wm[w]) {
                wm[w]--
            } else {
                break;
            }
        }
        if (Object.values(wm).every(n => n === 0)) ans.push(i);
    }
    return ans;
};

复杂度:

时间复杂度: O(mn) m为s长度,n为words长度
空间复杂度: O(m+n)

ShawYuan97 commented 2 years ago

前置知识

公司

思路

  1. 找到子串 1.1 找到子词 1.2 确定子词集合==目标words
  2. 移动子串 2.2 比较第一个单词和紧跟在后面的一个单词
  3. 移动索引 3.1 寻找新的子串

    关键点

代码

Python3 Code:


class Solution:
    import copy 
    def Iterative_string(self,s:str,words,start,word_len):
        res = []
        tmp_words = copy.deepcopy(words)
        if start+len(''.join(words)) > len(s):
            return res
        i = start
        while s[start:start+word_len] in tmp_words:
                tmp_words.remove(s[start:start+word_len])
                start += word_len
        if not tmp_words:
            res.append(i)
            while start + word_len <= len(s) and s[start:start+word_len] == s[i:i+word_len]:
                res.append(i)
                i += word_len
                start += word_len
        return res

    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        """
        串联所有单词的子串
        """
        res  = []
        left = 0
        word_len = len(words[0])
        for i in range(len(s)):
            if i+word_len*len(words) <= len(s) and s[i:i+word_len] in words:
                res.extend(self.Iterative_string(s,words,i,word_len))
            else:
                continue
        return list(set(res))

复杂度分析

令 n 为数组长度。

思路

思路和上面相似 但是实现时采用了哈希表来确定子串

关键点

代码

Python3 Code:


class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        allWords = collections.Counter(words)
        wordNum = len(words)
        wordLen = len(words[0])
        res = []
        for i in range(len(s) - wordNum * wordLen + 1):
            subWords = collections.defaultdict(int)
            index = i
            while index < i + wordNum * wordLen:
                curWord = s[index: index + wordLen]
                if curWord not in allWords or subWords[curWord] == allWords[curWord]:
                    break
                subWords[curWord] += 1
                index += wordLen
            if index == i + wordNum * wordLen:
                res.append(i)
        return res

复杂度分析

令 n 为数组长度。

MichaelXi3 commented 2 years ago

Idea

brainlds commented 2 years ago

class Solution { public List findSubstring(String s, String[] words) { HashMap<String,Integer> map = new HashMap<>(); int n = s.length(); int sl = words[0].length(); List res = new ArrayList<>();

    if (n < sl){
        return new ArrayList<>();
    }

    for (String str : words) {
        map.put(str,map.getOrDefault(str,0)+1);
    }

    for (int i = 0; i < n; i++) {
        int x = i;
        if (i + sl * words.length > n)
            break;
        String str = s.substring(x,x+sl);
        HashMap<String,Integer> tmp = new HashMap<>(map);

        while (!tmp.isEmpty() && tmp.containsKey(str)) {
            x = x + sl;
            tmp.put(str,tmp.get(str) - 1);
            if (tmp.get(str) == 0){
                tmp.remove(str);
            }
            if (x + sl > n) {
                break;
            }
            str = s.substring(x,x+sl);
        }

        if (tmp.isEmpty()){
            res.add(i);
        }
    }
    return res;
}

}

oneline-wsq 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 []
        one_word=len(words[0]) # 一个字符的长度
        word_num=len(words)  # words中一共有多少个字符
        n=len(s) 
        words=Counter(words)
        res=[]
        for i in range(0,one_word): # 这里的range范围为什么是(0,one_word)?
            cur_cnt=0
            left=i
            right=i
            cur_Counter=Counter()
            while right+one_word<=n:
                w=s[right:right+one_word]
                right+=one_word # right加上一个one_word的长度
                if w not in words:
                    left=right
                    cur_Counter.clear()
                    cur_cnt=0
                else:
                    cur_Counter[w]+=1 # 用Counter来计数
                    cur_cnt+=1 # 组成cur_Counter中的所有单词个数
                    while cur_Counter[w]>words[w]:
                        left_w=s[left:left+one_word]
                        left+=one_word
                        cur_Counter[left_w]-=1 
                        cur_cnt-=1
                    if cur_cnt==word_num:
                        res.append(left)
        return res

复杂度分析

时间复杂度:O(n)

空间复杂度:O(n)

CarrieyqZhang commented 2 years ago
public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();
        if (s == null || s.length() == 0 || words == null || words.length == 0) return res;
        HashMap<String, Integer> map = new HashMap<>();
        int one_word = words[0].length();
        int word_num = words.length;
        int all_len = one_word * word_num;
        for (String word : words) {
            map.put(word, map.getOrDefault(word, 0) + 1);
        }
        for (int i = 0; i < s.length() - all_len + 1; i++) {
            String tmp = s.substring(i, i + all_len);
            HashMap<String, Integer> tmp_map = new HashMap<>();
            for (int j = 0; j < all_len; j += one_word) {
                String w = tmp.substring(j, j + one_word);
                tmp_map.put(w, tmp_map.getOrDefault(w, 0) + 1);
            }
            if (map.equals(tmp_map)) res.add(i);
        }
        return res;
    }

time/space complexity: O(N)

BiN214 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;
    }
}
Size-of commented 2 years ago

代码

JavaScript Code:


/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    let map = new Map()
    words.forEach(o => map.set(o, (map.get(o) || 0) + 1))
    const n = words.length
    const len = words[0].length
    const res = []

    for (let i = 0; i <= s.length - n * len; i++) {
        let subMap = {}
        let num = 0
        while (num < n) {
            let cur = s.substr(i + num * len, len)
            if (!map.has(cur) || subMap[cur] === map.get(cur)) break
            subMap[cur] = (subMap[cur] || 0) + 1
            num++
        }
        if (num === n) {
            res.push(i)
        }   
    }
    return res

};

复杂度分析

令 n 为数组长度。

MoonLee001 commented 2 years ago

var findSubstring = function(s, words) { const wordLen = words[0].length; const wLen = words.length; let res = []; let map = new Map(); for (let i = 0; i < wLen; i++) { map.set(words[i] , (map.get(words[i]) || 0) + 1); }

let right = 0;
const n = s.length;
while (right < n) {
    let left = right;
    const subWord = new Map();
    while (left < right + wLen * wordLen) {
        const cur = s.substr(left, wordLen);
        if (!map.has(cur) || subWord.get(cur) == map.get(cur)) {
            break;
        }
        subWord.set(cur, (subWord.get(cur) || 0) + 1);
        left += wordLen;
    }
    if (left == right + wLen * wordLen) {
        res.push(right);
    }
    right++;
}
return res;

};

YuyingLiu2021 commented 2 years ago
class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if len(words) == 0 or len(s) == 0:
            return []

        len_word = len(words[0])
        res = []
        for i in range(len(s) - len_word * len(words) + 1):
            copy_words = words.copy()
            k = i
            while len(copy_words) != 0:
                if s[k : k+len_word] in copy_words:
                    copy_words.remove(s[k : k+len_word])
                    k += len_word
                else:
                    break
            if len(copy_words) == 0:
                res.append(i)

        return res
revisegoal commented 2 years ago

哈希表 + 滑动窗口

revisegoal commented 2 years ago

哈希表 + 滑动窗口

Ellie-Wu05 commented 2 years ago

思路 HashTable,Pointer

参考CSDN答案 先使用一个字典统计一下words中每个单词的数量。由于每个单词的长度一样,以题中给的例子而言,可以3个字母3个字母的检查,如果不在字典中,则break出循环。有一个技巧是建立一个临时字典curr,用来统计S中那些在L中的单词的数量,必须和L中单词的数量相等,否则同样break

代码

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        res, dict = [] ,{}
        n_word = len(words)
        n_s = len(s)

        if words:
            word_length = len(words[0])
        else:
            return res
        # 统计单词出现的次数
        for i in range(n_word):
            if words[i] in dict:
                dict[words[i]] +=1
            else:
                dict[words[i]] = 1
        #关于这个for里面的+1,你可以假设length_s与word_num * length_word一样那么很明显应该进入循环判断
        for i in range(n_s - n_word*word_length + 1):
            #curr用来统计小循环内出现相同单词次数,如果大于单词组内该单词出现数直接break
            curr,j = {},0
            #小循环,j相当于指向一个单词,每次移动一个单词,截取s内一个单词的长度判断是都在dict中
            while j < n_word:
                word = s[i+j*word_length : i+j*word_length + word_length]
                if not word in dict: break
                if not word in curr: 
                    curr[word] = 1
                else:
                    curr[word] +=1
                if curr[word] > dict[word]:
                    break
                j+=1
            #出小循环后j(单词数)等于单词数组数那就是刚好匹配,保存i
            if j == n_word:
                res.append(i)

        return res
wenliangchen commented 2 years ago

Code

class Solution {
    private HashMap<String, Integer> wordCount = new HashMap<String, Integer>();
    private int wordLength;
    private int substringSize;
    private int k;

    private boolean check(int i, String s) {
        // Copy the original dictionary to use for this index
        HashMap<String, Integer> remaining = new HashMap<>(wordCount);
        int wordsUsed = 0;

        // Each iteration will check for a match in words
        for (int j = i; j < i + substringSize; j += wordLength) {
            String sub = s.substring(j, j + wordLength);
            if (remaining.getOrDefault(sub, 0) != 0) {
                remaining.put(sub, remaining.get(sub) - 1);
                wordsUsed++;
            } else {
                break;
            }
        }

        return wordsUsed == k;
    }

    public List<Integer> findSubstring(String s, String[] words) {
        int n = s.length();
        k = words.length;
        wordLength = words[0].length();
        substringSize = wordLength * k;

        for (String word : words) {
            wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
        }

        List<Integer> answer = new ArrayList<>();
        for (int i = 0; i < n - substringSize + 1; i++) {
            if (check(i, s)) {
                answer.add(i);
            }
        }

        return answer;
    }
}
winrunwang commented 2 years ago

class Solution { public List findSubstring(String s, String[] words) { int len = s.length(); int wordsNum = words.length; int wordLen = words[0].length(); Map<String, Integer> map = new HashMap<>(); List res = new LinkedList<>(); for (int i = 0; i < wordsNum; i++) { map.put(words[i], map.getOrDefault(words[i], 0) + 1); } for (int i = 0; i < len - wordLen * wordsNum + 1; i++) { int left = i; Map<String, Integer> tmp = new HashMap<>(); int count = 0; while (left + wordLen <= len) { String word = s.substring(left, left + wordLen); if (!map.containsKey(word)) { break; } tmp.put(word, tmp.getOrDefault(word, 0) + 1); if (tmp.get(word) > map.get(word)) { break; } count++; left += wordLen; } if (count == wordsNum) { res.add(i); } } return res; } }

Zhen-Guan commented 2 years ago
class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if not s or not words:
            return []
        n = len(s)
        one_word_len = len(words[0])        #一个单词的长度
        word_cnt = len(words)               #单词的个数
        all_len = one_word_len * word_cnt

        words_cnt = defaultdict(int)        #每个单词出现的次数
        for w in words:
            words_cnt[w] += 1

        res = [] 
        for i in range(one_word_len):       #shell希尔排序的套路 start  delta = 一个单词的长度
            cur_w_num = 0                     #单词个数
            L, R = i, i
            cur_w_dic = defaultdict(int)      #当前单词的次数统计
            while R + one_word_len <= len(s):
                w = s[R: R + one_word_len]
                R += one_word_len           #R每次像右移一个单词的长度 类似于shell排序中的delta

                if w not in words_cnt:          #结束前,出现个不知道哪里的单词,整个前面一片就废了
                    L = R
                    cur_w_dic.clear()
                    cur_w_num = 0
                else:
                    cur_w_dic[w] += 1
                    cur_w_num += 1
                    while cur_w_dic[w] > words_cnt[w]:
                        L_w = s[L : L + one_word_len]
                        L += one_word_len       #L右移
                        cur_w_dic[L_w] -= 1       #做好统计
                        cur_w_num -= 1
                    if cur_w_num == word_cnt:     #如果说ok了
                        res.append(L)

        return res
xixiao51 commented 2 years ago

Idea

Sliding window.

Code

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<>();
        if (s == null || s.length() == 0 || words == null || words.length == 0) return res;
        HashMap<String, Integer> map = new HashMap<>();
        int wordLen = words[0].length();
        int size = words.length;
        int len = wordLen * size;
        for (String word : words) {
            map.put(word, map.getOrDefault(word, 0) + 1);
        }
        for (int i = 0; i < wordLen; i++) {
            int left = i, right = i, count = 0;
            HashMap<String, Integer> window = new HashMap<>();
            while (right + wordLen <= s.length()) {
                String r = s.substring(right, right + wordLen);
                right += wordLen;
                if (!map.containsKey(r)) {
                    count = 0;
                    left = right;
                    window.clear();
                } else {
                    window.put(r, window.getOrDefault(r, 0) + 1);
                    count++;
                    while (window.getOrDefault(r, 0) > map.getOrDefault(r, 0)) {
                        String l = s.substring(left, left + wordLen);
                        count--;
                        window.put(l, window.getOrDefault(l, 0) - 1);
                        left += wordLen;
                    }
                    if (count == size) {
                        res.add(left);
                    }
                }
            }
        }
        return res;
    }
}

Complexity Analysis

carterrr commented 2 years ago

class Solution { public List findSubstring(String s, String[] words) { int step = words[0].length(); int size = words.length; int window = step * size; int sLen = s.length(); List res = new ArrayList<>(); if(window > sLen) return res; Map<String, Integer> target = new HashMap<>(); for(String w : words) { target.put(w, target.getOrDefault(w, 0) + 1); } Map<String, Integer> wordInWin = new HashMap<>(); int l = 0, r = 0; // 初始化窗口 while(r < window) { String word = s.substring(r , r + step); wordInWin.put(word, wordInWin.getOrDefault(word, 0) + 1); r += step; } if(match(wordInWin, target)) res.add(l); // 添加完成先判断一次

    // 同步移动 看窗口是否满足
    while(r < sLen) { // 左闭右开 r不可达  因此r可到最大 + 1  仅仅作为不可达边界
        wordInWin = new HashMap<>();
        l ++;
        r ++;
        int tmp = l;
        while(tmp < r) {
            String w = s.substring(tmp, tmp + step);
            wordInWin.put(w, wordInWin.getOrDefault(w, 0) + 1);
            tmp += step;
        }
        if(match(wordInWin, target)) res.add(l); // 每次移动完判断一次
    }

    return res;
}

private boolean match(Map<String, Integer> window, Map<String, Integer> target) {
    for(String word : target.keySet()) {
        if(!target.get(word).equals(window.get(word)) ) return false;
    }
    return true;
}

}

miss1 commented 2 years ago

思路

先用一个map存储words中的字符,字符为key,数量为value。计算words总长度 words[0].length*words.length。

遍历字符串s,从当前位置left开始截取长度为words总长度的字符串y,将y按words[0]的长度分解出单词,将分解出的单词存储到map2中。

比较map和map2中每个单词的数量。

代码

var findSubstring = function(s, words) {
  let map = new Map(), map2 = new Map();
  for (let i = 0; i < words.length; i++) {
    if (map.has(words[i])) map.set(words[i], map.get(words[i]) + 1);
    else map.set(words[i], 1);
  }
  let single = words[0].length;
  let wordsLength = single * words.length;
  let left = 0, res = [];
  while (left < s.length - wordsLength + 1) {
    let end = wordsLength + left, start = left;
    map2.clear();
    for (; start < end; start += single) {
      let key = s.substr(start, single);
      if (!map.has(key)) break;
      if (map2.has(key)) map2.set(key, map2.get(key) + 1);
      else map2.set(key, 1);
      if (map2.get(key) > map.get(key)) break;
    }
    if (start === end) res.push(left);
    left++;
  }
  return res;
};

复杂度

time: O(n m k) n: s的长度,m: words的长度,k: 单个word的长度

space: O(m), m为words长度

physicshi commented 2 years ago

思路

计算 words 段的总长度,根据 words 维护 hash 表,需要在目标 string 里按着 words 总长度去遍历子串,判断子串里是否存在 hash 表的字段,若存在,hash 表中对应字段值减 1,直到对应字段值为 0,表示该字段已经用完;若不存在,则放弃该子串,右移遍历下一个子串

代码

var findSubstring = function (s, words) {
  const wordSize = words[0].length;
  const substringLen = wordSize * words.length;

  const wordsCount = {};
  words.forEach((w) => (wordsCount[w] = (wordsCount[w] || 0) + 1));

  const res = [];
  for (let i = 0; i <= s.length - substringLen; i++) {
    const tempCount = { ...wordsCount };
    let count = words.length;

    for (let j = i; j < i + substringLen; j += wordSize) {
      const word = s.slice(j, j + wordSize);

      if (!(word in tempCount) || tempCount[word] <= 0) break;

      tempCount[word]--;
      count--;
    }

    if (count === 0) res.push(i);
  }
  return res;
};

复杂度

xiayuhui231 commented 2 years ago

题目

串联所有单词的子串 https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/comments/

代码

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        if(s.empty() || words.empty()) return {};
        int n = words.size(), m = words[0].size(),j=0;
        if(s.size() < m*n) return {};
        vector<int> res;
        unordered_map<string,int>mp,tmp;
        for(auto str: words){
            mp[str]++;
        }
        string str = "";
        for(int i = 0; i+m*n<= s.size();i++){
            for( j = i;j<i+m*n;j+=m){
                str =  s.substr(j,m);
                if(mp.find(str) == mp.end()) break;
                tmp[str]++;
            }
            if(j == i+m*n && tmp == mp) res.push_back(i);
            tmp.clear();
        }
        return res;

    }
};

复杂度

时间复杂度:O(n^m) 空间复杂度:O(n)

PFyyh commented 2 years ago

思路

定义一个map,存储单词和出现个数。遍历字符串s,只能一位一位的移动。左窗口遍历的下标,右窗口为i+目标字符串数组的字符长度。然后在里面再裁剪,并存储到临时map,这是为了记录单词出现个数,防止单词超出需要的个数。结束以后,当左窗口能到达右边时,则表示该部分是目标字符串。

代码

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> result = new ArrayList<>();
        if (s == null || s.length() == 0 || words.length == 0) {
            return result;
        }
        //单个单词长度
        int oneWordLength = words[0].length();
        //单词个数
        int wordNum = words.length;
        //总长度
        int allWordsLength = oneWordLength * wordNum;
        HashMap<String, Integer> wordMap = new HashMap<>();
        //记录每个单词出现次数
        for (String word : words) {
            wordMap.put(word, wordMap.getOrDefault(word, 0) + 1);
        }
        //开始遍历
        for (int i = 0; i < s.length() - allWordsLength + 1; i++) {
            //建立临时map
            HashMap<String, Integer> tmp = new HashMap<>();
            //定义左指针
            int leftSide = i;
            int rightSide = leftSide + allWordsLength;
            //判断窗口内
            while (leftSide < rightSide) {
                //裁剪单词
                String compareWord = s.substring(leftSide, leftSide + oneWordLength);
                //如果不存在,右移窗口
                if (!wordMap.containsKey(compareWord)) {
                    break;
                }
                //如果单词超过了,抛弃
                if (tmp.get(compareWord)==wordMap.get(compareWord)) {
                    break;
                }
                //存储到临时map,这里记录个数是为了比较单词数是否超出
                tmp.put(compareWord, tmp.getOrDefault(compareWord, 0) + 1);
                //窗口移动一个单词长度
                leftSide+=oneWordLength;
            }
            //如果窗口能够移动到右边界(整段都符合)
            if (leftSide==i+allWordsLength){
                result.add(i);
            }
        }
        return result;
    }
}

复杂度

时间复杂度O(n^2)

空间复杂度O(n)

kbfx1234 commented 2 years ago

30. 串联所有单词的子串

// 4-23 cpp
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        if (s.empty() || words.empty()) return {};
        int len = s.size();
        unordered_map<string, int> mp;
        int a = 0;
        vector<int> ans;
        for (auto w : words) {
            ++mp[w];
        }
        int wlen = words[0].size(), count = words.size();
        int match = 0;
        for (int i = 0; i < len - wlen * count + 1; i++) {
            string cur = s.substr(i, wlen * count);
            unordered_map<string, int> temp;
            int j = 0, cnt = 0;
            for (; j < cur.size(); j += wlen) {
                string curword = cur.substr(j, wlen);

                if (mp.count(curword) == 0) break;
                temp[curword]++;
                cnt++;
                if (temp[curword] > mp[curword]) break;

                if (cnt == count) ans.push_back(i);
            }
        }
        return ans;
    }
};
duantao74520 commented 2 years ago

思路:

  1. 如果子串的字符数能够满足要求,判断里面的单词是否符合要求

    1. 将words保存到hash1表中,key为word,value为出现的次数

      image.png

    2. 遍历子串s,遍历每个单词,保存至hash2表中。

      1. 如果单词不在hash表1中,则返回false
      2. 如果单词的数量超过了hash表1的数量

代码:

class Solution {
public:
    bool IsVectorEque(const string& s) {
        //cout << "s = " << s << endl;
         std::map<string, int> hash2;
        for (int i =0 ;i <= s.size() - word_len; i+=word_len) {
            string sub_str = s.substr(i, word_len);
           // cout << "sub_str = " << sub_str << endl;
            if (hash1.find(sub_str) != hash1.end()) { // 单词次数大于当前已有次数
                hash2[sub_str] ++;
                if (hash2[sub_str] > hash1[sub_str] ) {
                   // cout << "s = " << s << " key > " << sub_str << endl;
                    return false;
                }
            } else { // 单词不存在
              //  cout << "s = " << s << " key not in " << sub_str << endl;
                return false;
            }
        }
        return true;
    }
    vector<int> findSubstring(string s, vector<string>& words) {
        // 字符串的hash表
        int word_num = 0;
        if (words.size() > 0) {
            word_len = words[0].size();
        }
        for (const auto& word : words) {
            hash1[word]++;
            word_num += word_len;
        }
        std::vector<int> ret_vec;
        if (s.size() < word_num) {
            return ret_vec;
        }
        for (int i =0 ; i<= s.size() - word_num; i++) {
            if (IsVectorEque(s.substr(i, word_num))) {
                ret_vec.emplace_back(i);
            }
        }
        return ret_vec;
    }
private:
    std::map<string, int> hash1; // 单词与频次的hash表 
    int word_len;        
};

复杂度:

时间:O(N2)

空间: O(N)

577961141 commented 2 years ago

题目思路

很简单的,直接用一个数组来存储就行

题目的题解code

<?php

class MyHashSet {
    /**
     * @var array
     */
    private $hash;

    /**
     */
    function __construct() {
        $this->hash = [];
    }

    /**
     * @param Integer $key
     * @return NULL
     */
    function add($key) {
        $this->hash[$key] = '';
        return null;
    }

    /**
     * @param Integer $key
     * @return NULL
     */
    function remove($key) {
        unset($this->hash[$key]);
        return null;
    }

    /**
     * @param Integer $key
     * @return Boolean
     */
    function contains($key) {
        return isset($this->hash[$key]);
    }
}

/**
 * Your MyHashSet object will be instantiated and called as such:
 * $obj = MyHashSet();
 * $obj->add($key);
 * $obj->remove($key);
 * $ret_3 = $obj->contains($key);
 */

时间和空间复杂度

时间复杂度:O(n) 空间复杂度:O(n)

zhulin1110 commented 2 years ago

题目地址(30. 串联所有单词的子串)

https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/

题目描述

给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。

 

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。

示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]

示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]

 

提示:

1 <= s.length <= 104
s 由小写英文字母组成
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 由小写英文字母组成

前置知识

思路

使用滑动窗口,当窗口的大小和words的长度一致时,使用map存储该窗口根据单词长度划分的单词,

将map和words构成的map进行对比,满足要求则输出left。

关键点

代码

JavaScript Code:


/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    let left = 0, right = 0;
    let slen = s.length;
    let wordLen = words[0].length;
    let wordNum = words.length;
    let wlen = wordNum * wordLen;
    let wordMap = new Map();
    for (let word of words) {
        let count = wordMap.has(word) ? wordMap.get(word) : 0;
        wordMap.set(word, count + 1);
    }
    let res = [];
    while (right < slen) {
        right++;
        if (right - left === wlen) {
            if (match(s.substring(left, right), wordMap, wordNum, wordLen)) {
                res.push(left);
            }
            left++;
        }
    }
    return res;
};

function match(str, wordMap, wordNum, wordLen) {
    let map = new Map();
    for (let i = 0; i < wordNum; i++) {
        let word = str.substring(i * wordLen, (i + 1) * wordLen);
        let count = map.has(word) ? map.get(word) : 0;
        map.set(word, count + 1);
    }
    let matchflag = true;
    for (let [key, value] of wordMap) {
        if (!map.has(key) || map.get(key) !== value) {
            matchflag = false;
        }
    }
    return matchflag;
}

复杂度分析

令 n 为数组长度。

flaming-cl commented 2 years ago

30. substring-with-concatenation-of-all-words

Solution: hash

To make this question a bit easier, we can first transform the input string into something more friendly for data handling —— hash map
As the output order is not important, I will use hash maps here.

  • Transform「words」into a hash map A
  • Split the input string into substrings and store each word of the substrings in hash map B
  • Match hash map A and B
  • if matched, store the index of each substring's first letter

Complexity

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

FutureFields commented 2 years ago

class Solution { public: vector findSubstring(string s, vector& words) { if (s.empty() || words.empty()) return {}; int n = words.size(), m = words[0].size(), j = 0; if (s.size() < m n) return {}; vector res; unordered_map<string, int> mp, tmp; for (auto str : words) { ++mp[str]; } string str = ""; for (int i = 0; i + m n <= s.size(); ++i) { for (j = i; j < i + m n; j += m) { str = s.substr(j, m); if (mp.find(str) == mp.end()) break; ++tmp[str]; } if (j == i + m n && tmp == mp) res.push_back(i); tmp.clear(); } return res; } };

Space:O(n), Time:O(n)

sallyrubyjade commented 2 years ago

代码

/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    if (!words || !words.length) return[];

    let wordLen = words[0].length;
    let allWordsLen = wordLen * words.length;
    let ans = [], wordMap = {};
    for (let w of words) {
        wordMap[w] ? wordMap[w]++ :wordMap[w] = 1
    }
    for (let i = 0; i < s.length - allWordsLen + 1; i++) {
        let wm = Object.assign({}, wordMap);

        for (let j = i; j < i + allWordsLen - wordLen + 1; j += wordLen) {
            let w = s.slice(j, j + wordLen);
            if (wm[w]) {
                wm[w]--
            } else {
                break;
            }
        }
        if (Object.values(wm).every(n => n === 0)) ans.push(i);
    }
    return ans;
};
LQyt2012 commented 2 years ago

思路

map存储words的单词统计个数,然后再字符串寻找排列组合,由于words中的单词长度固定,每一个可以以固定长度增长左右指针。

代码

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        from collections import Counter
        if not s or not words:return []
        word_length = len(words[0])
        word_counts = len(words)
        n = len(s)
        if n < word_length:return []
        words_map = Counter(words)
        res = []
        for i in range(0, word_length):
            cur_cnt = 0
            left = i
            right = i
            cur_Counter = Counter()
            while right + word_length <= n:
                w = s[right:right + word_length]
                right += word_length
                if w not in words_map:
                    left = right
                    cur_Counter.clear()
                    cur_cnt = 0
                else:
                    cur_Counter[w] += 1
                    cur_cnt += 1
                    while cur_Counter[w] > words_map[w]:
                        left_w = s[left:left+word_length]
                        left += word_length
                        cur_Counter[left_w] -= 1
                        cur_cnt -= 1
                    if cur_cnt == word_counts :
                        res.append(left)
        return res

复杂度分析:

时间复杂度:O(N)
空间复杂度:O(1)

KelvinG-LGTM commented 2 years ago

思路

滑动窗口 + 哈希表

代码

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if not s or not words:return []
        one_word = len(words[0])
        word_num = len(words)
        n = len(s)
        words = Counter(words)
        res = []
        for i in range(0, one_word):
            cur_cnt = 0
            left = i
            right = i
            cur_Counter = Counter()
            while right + one_word <= n:
                w = s[right:right + one_word]
                right += one_word
                cur_Counter[w] += 1
                cur_cnt += 1
                while cur_Counter[w] > words[w]:
                    left_w = s[left:left+one_word]
                    left += one_word
                    cur_Counter[left_w] -= 1
                    cur_cnt -= 1
                if cur_cnt == word_num :
                    res.append(left)
        return res

复杂度分析

时间复杂度

O(n)

空间复杂度

O(n)

Joyce94 commented 2 years ago

思路 暴力解有两种思路: 1、从words入手,排序组合生成xx种字符串,查看各个字符串在s中的位置 排序组合的时间开销是 (words.length)! / (words 中单词重复次数相乘), 时间复杂度为 O(m!), m 为 words 长度。阶乘的时间复杂度基本不可能通过 2、从s入手,遍历每个长度为 words.lengthlen(words[0]) 的字符串,转为子问题,判断长度为 words.lengthlen(words[0]) 的字符串是否可以由words组成 关键问题:如何判断 s 的子串 Y 是否可以由 words 数组的构成

VictorHuang99 commented 2 years ago

Idea:暴力穷举

Code: ''' var findSubstring = function(s, words) { if (!words || !words.length) return[]; let wordLen = words[0].length; let allWordsLen = wordLen * words.length; let ans = [], wordMap = {}; for (let w of words) { wordMap[w] ? wordMap[w]++ :wordMap[w] = 1 } for (let i = 0; i < s.length - allWordsLen + 1; i++) { let wm = Object.assign({}, wordMap); for (let j = i; j < i + allWordsLen - wordLen + 1; j += wordLen) { let w = s.slice(j, j + wordLen); if (wm[w]) { wm[w]-- } else { break; } } if (Object.values(wm).every(n => n === 0)) ans.push(i); } return ans; }; ''' 时间复杂度O(n) 空间复杂度O(n)

m908 commented 2 years ago
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        int sn = s.size(), n = words.size(), m = words[0].size();
        unordered_map<string, int> wc;
        vector<int> ans;
        for(auto& w : words) wc[w]++;
        for(int i = 0; i < m; ++i){
            int l = i, r = i, num = 0;
            unordered_map<string, int> wc0;
            while(r + m <= sn){
                auto back = s.substr(r, m);
                r += m;
                if(!wc.count(back)){
                    l = r;
                    num = 0;
                    wc0.clear();
                } else {
                    num++;
                    wc0[back]++;
                    while(wc[back] < wc0[back]){
                        auto front = s.substr(l, m);
                        wc0[front]--;
                        num--;
                        l += m;
                    }
                    if(num == n) ans.push_back(l);
                }
            }
        }

        return ans;
    }
};
xingchen77 commented 2 years ago

思路

使用两个指针

代码

    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        allWords = collections.Counter(words)
        wordNum = len(words)
        wordLen = len(words[0])
        res = []
        for i in range(len(s) - wordNum * wordLen + 1):
            subWords = collections.defaultdict(int)
            index = i
            while index < i + wordNum * wordLen:
                curWord = s[index : index + wordLen]
                if curWord not in allWords or subWords[curWord] == allWords[curWord]:
                    break
                subWords[curWord] += 1
                index += wordLen
            if index == i + wordLen * wordNum:
                res.append(i)
        return res 

复杂度

时间 O(n∗m),n为字符串长度,m为单词个数 \ 空间 O(m)

yaya-bb commented 2 years ago

题目地址(30. 串联所有单词的子串)

https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/

题目描述

给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。

 

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。

示例 2:

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]

示例 3:

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]

 

提示:

1 <= s.length <= 104
s 由小写英文字母组成
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i] 由小写英文字母组成

前置知识

代码

JavaScript Code:


/**
 * @param {string} s
 * @param {string[]} words
 * @return {number[]}
 */
var findSubstring = function(s, words) {
    const wordSize = words[0].length;
    const substringLen = wordSize * words.length;

    const wordsCount = {};
    words.forEach(w => (wordsCount[w] = (wordsCount[w] || 0) + 1))

    const res = []
    for (let i = 0; i <= s.length - substringLen; i++) {
        const tempCount = {...wordsCount};
        let count = words.length;

        for (let j = i; j < i + substringLen; j += wordSize) {
            const word = s.slice(j, j + wordSize);

            if (!(word in tempCount) || tempCount[word] <= 0) 
            break;

            tempCount[word]--;
            count--;
        }

        if (count === 0) 
        res.push(i);
    }
    return res;
};

复杂度分析

令 n 为数组长度。

youxucoding commented 2 years ago

4月23日

【day23】

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

难度困难648

给定一个字符串 s 和一些 长度相同 的单词 words 找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。

示例 1:

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。

思路:

滑动窗口思想,因为word长度相同,维护一个『word长度』* 『words长度』的窗口,判断窗口内的words与输入的words是否相同,这点可以使用HashMap。

代码实现:

class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList();
        int wordLen = words[0].length();
        int len = wordLen*words.length;
        HashMap<String,Integer> need = new HashMap();
        HashMap<String,Integer> window = new HashMap();
        for(String ss : words){
            need.put(ss,need.getOrDefault(ss,0)+1);

        }

        if(s.length() < len){
            return res;
        }
        int left = 0;
        int right = left+len;
        int vaild = 0;
        while(right <= s.length()){
            for(int i = 1;i <= words.length;i++){
                String tmp = s.substring(left+(i-1)*wordLen,left+i*wordLen);
                if(!need.containsKey(tmp)){
                    break;
                }else{
                    window.put(tmp,window.getOrDefault(tmp,0)+1);
                    if(need.get(tmp).equals(window.get(tmp))){
                        vaild++;
                    }
                }

            }
            if(vaild == need.size()){
                res.add(left);
            }
            window.clear();
            vaild = 0;
            left++;
            right++;
        }
        return res;
    }
}

复杂度分析:

tensorstart commented 2 years ago

思路

滑动窗口

代码

var findSubstring = function(s, words) {
    let left = 0,right = 0,wordsLen = words.length;
    if(wordsLen == 0) return [];
    let res = [];
    let gapLen = words[0].length;
    let needs = {};
    let windows = {};
    for(let i = 0;i < wordsLen;i++){
        needs[words[i]] ? needs[words[i]]++ : needs[words[i]] = 1;
    }
    let needsLen = Object.keys(needs).length;
    let match = 0;
    for(let i = 0;i < gapLen;i++){
        right = left = i;
        match = 0;
        while(right <= s.length - gapLen){
            let c1 = s.substring(right,right + gapLen);
            right += gapLen;
            windows[c1] ? windows[c1]++ : windows[c1] = 1;
            if(windows[c1] === needs[c1]){
                ++match;
            }
            while(left < right && match == needsLen){
                if(Math.floor((right - left) / gapLen) == wordsLen){
                    res.push(left);
                }
                let c2 = s.substring(left,left + gapLen);
                left += gapLen;
                windows[c2]-- ;
                if(needs[c2] && windows[c2] < needs[c2]){
                    match--;
                }
            }
        }
        windows = {};
    }
    return res;
};

复杂度分析

时间复杂度O(n) 空间复杂度O(n)

KennethAlgol commented 2 years ago
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;
    }
}
Magua-hub commented 2 years ago


class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int> res;
        if (words.empty()) return res;
        int n = s.size(), m = words.size(), w = words[0].size();
        unordered_map<string, int> tot;
        for (auto& word : words) tot[word] ++ ;

        for (int i = 0; i < w; i ++ ) {
            unordered_map<string, int> wd;
            int cnt = 0;
            for (int j = i; j + w <= n; j += w) {
                if (j >= i + m * w) {
                    auto word = s.substr(j - m * w, w);
                    wd[word] -- ;
                    if (wd[word] < tot[word]) cnt -- ;
                }
                auto word = s.substr(j, w);
                wd[word] ++ ;
                if (wd[word] <= tot[word]) cnt ++ ;
                if (cnt == m) res.push_back(j - (m - 1) * w);
            }
        }

        return res;
    }
};