算法入门:滑动窗口--->找到字符串中所有的字母异位词,串联所有的子串,最小覆盖子串

🎬 胖咕噜的稞达鸭个人主页
🔥 个人专栏 : 《数据结构《C++初阶高阶》
《Linux系统学习》
《算法入门》

⛺️技术的杠杆,撬动整个世界!


找到字符串中所有的字母异位词

找到字符串中所有的字母异位词

找到异位词之后要返回在s字符串中的起始下标

算法原理:

如何快速判断两个字符串是否是"异位词"?

利用哈希表:

将要比较的p字符放到hash1中记录p的len长度m,以及每一个字符出现的次数;

在hash2中遍历,每遍历m个字符放到hash2中,跟hash1对比,如果每个字符的出现次数跟hash1中相等,就说明此时hash2中的字符串满足异位词特点,

返回首字符的下标。

随后要清空hash2,从left+1的位置进hash2。(这一步需要优化:不需要清空hash2,只需要将hash2的第一个字符丢出去,将hash2的下一个字符放进来,这样就少了重复字符的进入哈希表,可以节省效率)

也就是说这个滑动窗口的大小是固定的,固定大小的滑动窗口一个字符一个字符移动

解法:

1.left=0,right=0;

2.进窗口;(hash2[in]++)

  1. 判断 (right - left +1 > m)

出窗口 (hash2[out]--)

更新结果(check(hash2,hash1))

更新结果的判断条件:利用变量count来统计窗口中"有效字符"的个数
  1. 进窗口的时候维护:进入后hash2[in]<=hash1[in],就说明是有效字符->count++
  2. 出窗口:hash2[out]<=hash1[out]->有效字符->count--
  3. 更新结果:count=m
cpp 复制代码
class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> ret; // 用来存储返回结果,字符串起始地址
        int hash1[26] = {0};
        for (auto ch : p)
            hash1[ch - 'a']++; // 记录hash1中的字符
        int hash2[26] = {0};
        int m = p.size();
        for (int left = 0, right = 0, count = 0; right < s.size(); right++) {
            char in = s[right];//取出当前右边界的字符in(即将加入窗口的字符)
            if (++hash2[in - 'a'] <= hash1[in - 'a'])
                count++;//如果更新后的计数 ≤ hash1中该字符的计数(说明这个字符是p中包含的,且窗口内数量未超标),则count++(有效字符数 + 1)
            if (right - left + 1 > m) {
                char out = s[left++];//取出当前左边界的字符out(即将丢出窗口的字符,并将窗口缩小)
                if (hash2[out - 'a']-- <= hash1[out - 'a'])
                    count--;//如果移出前的计数 ≤ hash1中该字符的计数(说明这个字符原本是 "有效字符"),则count--(有效字符数 - 1);
            }
            //更新结果
            if (count == m)
                ret.push_back(left);
        }
        return ret;
    }
};

串联所有的子串:

串联所有的子串


算法原理

s="barfoothefoobarman"

w="foo","bar"

对于这个题目,寻找字串;可以先将w中的"foo"近似于a,"bar"近似于b,

cpp 复制代码
s=" bar     foo     the     foo      bar     man"
s=   b       a       c       a        b       d

解法:滑动窗口+哈希表

  1. 哈希表:
  2. Left和right指针的移动:移动的步长是单词的长度->len
  3. 滑动窗口执行的次数:从索引为0的位置len=3划分每一段的长度;从索引为1的位置len=3划分每一段的长度;从索引为2的位置len=3划分每一段的长度。从索引为3的位置len=3划分每一段的长度(有重复,所以就用三种方法划分每一段的长度)。
cpp 复制代码
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        vector<int>ret;//用来存储返回的下标
        unordered_map<string,int>hash1;//保存words里面所有单词的频次
        for(auto& ch:words)hash1[ch]++;

        int len=words[0].size(),m=words.size();
        for(int i=0;i<len;i++)
        {
            unordered_map<string,int>hash2;//维护窗口内单词的频次
            for(int left=i,right=i,count=0;right+len<=s.size();right+=len)
            {
                //进窗口+维护count
                string in =s.substr(right,len);//从right位置取长度为len的子串,即要加入窗口的单词in
                hash2[in]++;//将新加入的单词in计入hash2(窗口内该单词的词频+1)
                if(hash2[in]<=hash1[in])count++;//这个单词是words中包含的,且窗口内数量未超标(是 "有效" 的),因此count++
                //判断
                if(right-left+1>m*len)
                {
                    //出窗口+维护count
                    string out = s.substr(left,len);
                    if(hash2[out]<=hash1[out])count--;//说明这个单词原本是 "有效" 的,移出后有效数减少,count--;
                    hash2[out]--;
                    left +=len;
                }
                //更新结果
                if(count == m)ret.push_back(left);
            }
        }
        return ret;
    }
};

最小覆盖子串

最小覆盖子串

举例子

s: A D O B E C O D E B A N C

t: A B C

将t放入hash2中;

解法:

  1. left = 0 ,right = 0;
  2. 进窗口:(hash2[in]++)
  3. 判断:(check(hash1,hash2))
  4. 更新结果->起始位置,最短长度
  5. 出窗口:hash2[out]--,一直出窗口到不合法为止

优化:如果用两个哈希表,优化判断条件,这里通过一个变量count来标记有效字符的种类

t:ABC A:1 B:1 C:1

s:ABBCA

  1. 进窗口(进入之后,当hash2[in] == hash1[in],count++,有效字符++,继续判断);
  2. 出窗口(出窗口之前,当hash2[out] == hash1[out],count--,有效字符--,继续判断合适的字符串)
  3. 判断条件(count == hash1.size() ,统计哈希表的长度)
cpp 复制代码
class Solution {

public:

    string minWindow(string s, string t) {
        int hash1[128]={0};//统计字符串t中的每一个字符的频次
        int kinds=0;//统计有效字符的种类
        for(auto ch:t)
        {
            if(hash1[ch] == 0)kinds++;
            hash1[ch]++;//if(hash1[ch]++ == 0)kinds++;
        }
        int hash2[128]={0};//统计窗口内每一个字符出现的频次
        int minlen =INT_MAX,begin=-1;
        for(int left=0,right=0,count=0;right<s.size();right++)
        {
            char in = s[right];
            if(++hash2[in]==hash1[in])count++;
            while(count == kinds)//判断条件
            {
                if(right-left+1< minlen)//更新结果
                {
                    minlen = right -left +1;
                    begin =left;
                }
                char out =s[left++];
                if(hash2[out]-- == hash1[out])count--;
            }
        }
        if(begin == -1)return "";
        else return s.substr(begin,minlen);
    }
};
相关推荐
小青龙emmm1 小时前
2025级C语言第二次周测(国教专用)题解
c语言·开发语言·算法
SelectDB1 小时前
上海证券 SelectDB 升级实践:湖仓流批一体落地与 Elasticsearch 全面替换
数据库·apache
一个天蝎座 白勺 程序猿1 小时前
KingbaseES在政务领域的应用实践——武汉人社大数据平台“数字化服务新模式”
大数据·数据库·政务·kingbasees·金仓数据库
WolfGang0073211 小时前
代码随想录算法训练营Day28 | 509.斐波那契数列、70.爬楼梯、746.使用最小花费爬楼梯
算法
Boop_wu1 小时前
[Java EE] 多线程进阶(JUC)(2)
java·jvm·算法
m***92381 小时前
docker中配置redis
redis·docker·容器
f***01932 小时前
【MySQL】JDBC的连接
数据库·mysql
5***T4482 小时前
开启mysql的binlog日志
数据库·mysql
闻缺陷则喜何志丹2 小时前
【SOSDP模板 容斥原理 逆向思考】3757. 有效子序列的数量|分数未知
c++·算法·力扣·容斥原理·sosdp·逆向思考