LeetCode - 解题笔记 - 30 - Substring with Concatenation of All Words

Substring with Concatenation of All Words

Solution 1

由于给定字符串的长度和给定的待匹配词的数目都太大了,想要枚举所有的组合然后匹配字符串的暴力思路肯定是不太行。可是每个待匹配此的长度一定,那么我们可以确定的是待匹配的子串的长度是words.size()*words[0].size(),并且只要保证每一个words[0].size()的子子串匹配上其中一个待匹配词,且不重复匹配就可以判定存在了。因此遍历的思路就是从每一个位置开始(题目要求没办法)取一个words.size()*words[0].size()长度的子串,然后检查每words[0].size()长度的子子串是否满足一个待匹配词,然后中途存在不存在单词或者多出现(同一个词可能在待匹配序列中出现多次,如果出现过多肯定不行)则认为无法匹配,否则能够正好匹配(对于小于的情形不用担心,必然会有一个词多出现或者出现不存在词替代其位置)。为了进一步简化复杂度,使用哈希函数匹配取代匹配过程。

  • 时间复杂度: O ( M N n ) O(MNn) O(MNn),其中 M M M为输入字符串的长度, N N N为待匹配词的个数, n n n为每个词的长度
  • 空间复杂度: O ( N + n ) O(N + n) O(N+n),其中 N N N为待匹配词的个数, n n n为每个词的长度,分别为构造哈希表和取子串所消耗
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int>ans;
        if (s.size() < words.size() * words[0].size()) return ans;
        
        // 计算每个词在给定列表中的次数
        unordered_map<string, int> count;
        unordered_map<string, int>::iterator it;
        for (auto &word: words) {
            if ((it = count.find(word)) == count.end()) {
                count.insert(make_pair(word, 1));
            }
            else{
                count[word]++;
            }
        }
        
        for (int i = 0; i <= s.size() - words.size() * words[0].size(); ++i) {
            int j = 0;
            unordered_map<string, int> check;
            while (j <= words.size()) {
                if (j == words.size()) {
                    ans.push_back(i);
                    break; // n个情形都符合,保存
                }
                
                string tmpS = s.substr(i + words[j].size() * j, words[j].size());
                if ((it = count.find(tmpS)) == count.end()) {
                    break; // 中间有一个不存在的序列,跳出
                }
                else if ((it = check.find(tmpS)) == check.end()){
                    check.insert(make_pair(tmpS, 1));
                } 
                else {
                    check[tmpS]++;
                }
                
                if (check[tmpS] > count[tmpS]) break; // 有一个序列多了,跳出(少了不担心,必然有一个多了,或者出现一个不存在的)
                
                j++;
            }
        }
        
        return ans;
    }
};

Solution 2

来自网上的更简单的算法:滑动窗口详细通俗的思路分析,多解法 - 串联所有单词的子串 - 力扣(LeetCode) (leetcode-cn.com)

这个教程说的比较通俗移动,整体思路就是以单词长度为单位向后检查,并在上一个思路中提到的两种判负情形下华东检查窗口。为了能够遍历所有情形,将偏移量设置为单词长度内的所有值,进行遍历。

  • 时间复杂度: O ( M + N ) O(M+N) O(M+N),其中 M M M为输入字符串的长度, N N N为待匹配词的个数
  • 空间复杂度: O ( N + n ) O(N+n) O(N+n),其中 N N N为待匹配词的个数, n n n为每个词的长度,分别为构造哈希表和取子串所消耗
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int>ans;
        if (s.size() < words.size() * words[0].size()) return ans;
        
        // 计算每个词在给定列表中的次数
        unordered_map<string, int> count;
        unordered_map<string, int>::iterator it;
        for (auto &word: words) {
            if ((it = count.find(word)) == count.end()) {
                count.insert(make_pair(word, 1));
            }
            else{
                count[word]++;
            }
        }
        
        int lenWord = words[0].size();
        // 共计words[0].size()个偏移量
        for (int offset = 0; offset < lenWord; ++offset) {
            unordered_map<string, int> check;
            int numChecked = 0; // 当前check表里面已经记录的单词个数,永远是前numChecked个
            for (int i = offset; i <= s.size() - words.size() * lenWord; ) {
                bool flag = true;
                
                // 检查长度下后面剩下的一些单词整理到哈希表中
                while (numChecked < words.size()) {
                    // cout << i << " " << numChecked << " " << s.substr(i, words.size() * lenWord) << endl;
                    string word = s.substr(i + numChecked * lenWord, lenWord);
                    if ((it = count.find(word)) == count.end()) {
                        // 不匹配的词,一直向后移动到这个词从窗口前面移动出去
                        i += (numChecked + 1) * lenWord;
                        check.clear();
                        numChecked = 0;
                        break;
                    }
                    else {
                        if ((it = check.find(word)) == check.end()) {
                            check.insert(make_pair(word, 1));
                        }
                        else {
                            check[word]++;
                        }
                        numChecked++;
                        if (check[word] > count[word]) {
                            // 一个词多了,向后移动直到从窗口前面移动出一个相同的单词
                            int numRemoved = 0;
                            while (check[word] > count[word]) {
                                string tmpWord = s.substr(i + numRemoved * lenWord, lenWord);
                                check[tmpWord]--;
                                numRemoved++;
                            }
                            numChecked -= numRemoved;
                            i += numRemoved * lenWord;
                            flag = false;
                            // cout << numRemoved << " " << i << endl;
                            break;
                        }
                    }
                }
                
                if (numChecked == words.size()) {
                    ans.push_back(i);
                }
                
                if (numChecked > 0 && flag) {
                    // 需要向前移动,flag用于跳过出现相同词的时候多移动一个
                    string tmpWord = s.substr(i, lenWord);
                    check[tmpWord]--;
                    numChecked--;
                    i += lenWord;
                }
            }
        }
        
        return ans;
    }
};

Solution 3

Solution 1的Python实现

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        ans = list()
        numWord = len(words)
        lenWord = len(words[0])
        
        if len(s) < numWord * lenWord: return ans
        
        count = dict()
        for word in words:
            if word not in count:
                count[word] = 1
            else:
                count[word] += 1
                
        for i in range(len(s) - numWord * lenWord + 1):
            j = 0;
            check = dict()
            
            while j <= numWord:
                if j == numWord: 
                    ans.append(i)
                    break
                    
                tmpS = s[i + lenWord * j: i + lenWord * (j + 1)]
                # print(i, j, tmpS, s[i: i + lenWord * numWord])
                if tmpS not in count:
                    break
                elif tmpS not in check:
                    check[tmpS] = 1
                else:
                    check[tmpS] += 1
                    
                    if check[tmpS] > count[tmpS]: break
                        
                j += 1
                
        return ans

Solution 4

Solution 2的Python实现

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        ans = list()
        numWord = len(words)
        lenWord = len(words[0])
        
        if len(s) < numWord * lenWord: return ans
        
        count = dict()
        for word in words:
            if word not in count:
                count[word] = 1
            else:
                count[word] += 1
        
        for offset in range(lenWord):
            check = dict()
            numChecked = 0
            i = offset
            while i <= len(s) - numWord * lenWord:
                flag = True
                # print(offset, i, numChecked, word, s[i: i + numWord * lenWord])
                while numChecked < numWord:
                    word = s[i + numChecked * lenWord: i + (numChecked + 1) * lenWord]
                    if word not in count:
                        i += (numChecked + 1) * lenWord
                        check.clear()
                        numChecked = 0
                        break
                    else:
                        if word not in check:
                            check[word] = 1
                        else:
                            check[word] += 1
                            
                        numChecked += 1
                        
                        if check[word] > count[word]:
                            # print(offset, i)
                            numRemoved = 0
                            while check[word] > count[word]:
                                tmpWord = s[i + numRemoved * lenWord: i + (numRemoved + 1) * lenWord]
                                check[tmpWord] -= 1
                                numRemoved += 1
                            
                            numChecked -= numRemoved
                            i += numRemoved * lenWord
                            flag = False
                            break
                            
                if numChecked == numWord: 
                    ans.append(i)
                    # print("ss", offset, i, numChecked, word, s[i: i + numWord * lenWord])
                    
                if numChecked > 0 and flag:
                    
                    tmpWord = s[i: i + lenWord]
                    check[tmpWord] -= 1
                    numChecked -= 1
                    i += lenWord
                
        return ans
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值