当前位置: 首页 > article >正文

滑动窗口——串联所有单词的子串

一.题目描述

30. 串联所有单词的子串 - 力扣(LeetCode)

二.题目解析 

题目前提:s是一个字符串,words是一个字符串数组,里面所有的字符串的长度都是相等的。

题目要求:找到s中的一段连续的子串,该子串是由words中的所有字符串随机排列组合起来的,但是每一个字符串内部的顺序是不变的。然后返回该子串的起始位置即可。

三.算法原理

3.1问题转化

因为words中的每一个字符串都是一样长的,所以我们可以将s字符串进行划分,划分成与words中字符串一样长的形式:

然后我们将words中的两个字符串看作两个字符,那么s中的字符串也可以根据分组看成若干字符,这时,该题就转换成了昨天的那道题——找出所有的异位词,找出一个子区间,是由words组成的即可。

 所以这道题我们也可以使用滑动窗口来解决问题。

3.2滑动窗口

在使用滑动窗口之前,我们还要对一些细节进行分析:

细节1:

我们刚才划分是从首位置开始划分的,但是答案也可能这样分布:

所以我们要根据开始位置的不同,进行多次滑动窗口的过程。根据示例的分析,我们需要进行len次滑动窗口(len指words中每一个单词的长度) ,第len+1次滑动窗口就和第一个次一样了。

细节2:

我们这次哈希表里面存的就不再是字符了,而是字符串。所以我们要用容器unordered_map来实现。

细节3:

进窗口的时候,我们就不再是一次进一个元素了,而是进一段子区间。我们可以使用substr函数来获取子区间,该子区间的长度就是words中每一个单词的长度

对于这个示例就是将right后面的3个字符作为字符串插入到哈希表中。

细节4:

判断的逻辑,当子区间的长度大于words所有字符的和的时候就需要出窗口了,但是我们这里没有必要用子区间的所有长度与words总长度比较,只需用right-left+1与总长度比较即可。 

细节5:

出窗口的时候,我们让left后面的len个字符出窗口,而不是一个,与进窗口一致。

细节6:

left和right不再一次走一步,因为我们要将一段区间插入到哈希表中,所有left和right每次走len步。 

四.代码实现

// C++

// 写法一:
vector<int> ret;
int len = words[0].size();
int n = words.size();
if (s.size() < len * n)
    return ret;
unordered_map<string, int> mp_w;
for (auto& e : words) mp_w[e]++;

for (int i = 0; i < len; ++i)
{
    unordered_map<string, int> mp_s;
    for (int l = i, r = i, count = 0; r + len <= s.size(); r += len)
    {
        // 进窗口 + 维护count
        string s_in = s.substr(r, len);
        mp_s[s_in]++;
        if (mp_s[s_in] <= mp_w[s_in]) count++;

        // 判断
        if (r - l + 1 > len * n)
        {
            // 出窗口 + 维护count
            string s_out = s.substr(l, len);
            if (mp_s[s_out] <= mp_w[s_out]) count--;
            mp_s[s_out]--;
            l += len;
        }

        //更新结果
        if (count == n) ret.emplace_back(l);
    }
}
return ret;


// 写法二:
vector<int> ret;

int n = words.size(); // words中有几个单词
int len = words[0].size(); // 每个单词的长度
if (s.size() < len * n)
    return ret;
unordered_map<string, int> mp_w;
for (auto e : words) mp_w[e]++;

int i = 0;
while (i < len)
{
    unordered_map<string, int> mp_s;
    int count = 0;
    for (int left = i, right = i; right < s.size();)
    {
        // 进窗口
        if (right + len > s.size()) break;
        right += len;
        string s_in(s.begin() + right - len, s.begin() + right);
        mp_s[s_in]++;
        if (mp_s[s_in] <= mp_w[s_in]) count++;

        // 判断
        if (right - left > len * n)
        {
            string s_out(s.begin() + left, s.begin() + left + len);
            if (mp_s[s_out] <= mp_w[s_out]) count--;
            mp_s[s_out]--;
            left += len;
        }

        //更新结果
        if (count == n) ret.emplace_back(left);
    }
    ++i;
}
return ret;
# python

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        ret = [] # 返回列表
        cnt_w = Counter() # 记录words中单词的频次
        for e in words:
            cnt_w[e]+=1

        n,Len = len(words),len(words[0]) # words中单词的个数,以及长度

        for i in range(0,Len):
            cnt_s = Counter() # 窗口中单词的频次
            r,l,count = i,i,0
            while r+Len <= len(s):
                # 进窗口 + 维护 count
                s_in = s[r:r+Len]
                cnt_s[s_in] += 1
                if cnt_s[s_in] <= cnt_w[s_in]:
                    count+=1
                
                # 判断 
                if r-l+1>n*Len:
                    # 出窗口 + 维护 count
                    s_out = s[l:l+Len]
                    if cnt_s[s_out] <= cnt_w[s_out]:
                        count -= 1
                    cnt_s[s_out] -= 1
                    l += Len
                
                # 更新结果
                if count == n:
                    ret.append(l)
                r += Len
        return ret

http://www.kler.cn/a/474225.html

相关文章:

  • 人工智能-机器学习之多元线性回归(项目实践一)
  • 详细全面讲解C++中重载、隐藏、覆盖的区别
  • Docker中运行Qt应用程序——待继续研究
  • 计算机网络 笔记 物理层
  • 网络安全-XSS跨站脚本攻击(基础篇)
  • 【第01阶段-基础必备篇-第二部分--Python之基础】04 函数
  • Linux好用软件
  • C++ 入门第26天:文件与流操作基础
  • 记录一次MySQL:caching_sha2_password报错
  • Linux中增加swap分区
  • 比QT更高效的一款开源嵌入式图形工具EGT-Ensemble Graphics Toolkit
  • 【gRPC】对称与非对称加解密和单向TLS与双向TLS讲解与go案例
  • vue 点击按钮复制文本功能(同时解决http不安全问题)
  • c# readonly 和 const的区别和使用场景
  • Android配件应用默认启动与USB权限申请区别
  • CODESYS MODBUS TCP通信(禾川Q1 PLC作为MODBUS TCP从站)
  • 【mysql】流程控制
  • 【前端,TypeScript】TypeScript速成(八):Promise
  • 机器学习的组成
  • PDFMathTranslate: Star13.8k,一款基于AI的PDF文档全文双语翻译PDF文档全文双语翻译,保留格式神器,你应该需要它
  • R语言的语法
  • 《鸿蒙系统AI技术:筑牢复杂网络环境下的安全防线》
  • 模型评估指标总结(预测指标、分类指标、回归指标)
  • 【开源免费】基于Vue和SpringBoot的贸易行业crm系统(附论文)
  • TVbox 手机、智能电视节目一网打尽
  • HarmonyOS Next系列之华为账号一键登录功能实现(十四)