【优选算法】1. 水果成蓝 2.找到字符串中所有字母的异位词

🔥小龙报:个人主页

🎬作者简介:C++研发,嵌入式,机器人,AI等方向学习者

❄️个人专栏:《优选算法》

永远相信美好的事情即将发生

文章目录

  • 前言
  • 一、最大的连续1个数
    • 1.1题目
    • [1.2 算法原理](#1.2 算法原理)
      • [1.2.1 算法思路](#1.2.1 算法思路)
      • [1.2.2 算法流程](#1.2.2 算法流程)
    • [1.3 代码](#1.3 代码)
  • 二、找到字符串中所有字母的异位词
    • [2.1 题目](#2.1 题目)
    • [2.2 算法原理](#2.2 算法原理)
      • [2.2.1 算法思路](#2.2.1 算法思路)
      • [2.2.2 算法流程](#2.2.2 算法流程)
        • [2.2.2.1 法一](#2.2.2.1 法一)
        • [2.2.2.2 法二](#2.2.2.2 法二)
    • [2.3 代码](#2.3 代码)
      • [2.3.1 法一代码](#2.3.1 法一代码)
      • [2.3.2 法二代码](#2.3.2 法二代码)
  • 总结与每日励

前言

滑动窗口是算法面试高频考点,专门高效求解数组、字符串连续区间类题目,能将暴力解法 O (n²) 复杂度优化至线性 O (n)。本文选取两道经典例题:可变窗口题型水果成篮、定长窗口题型字母异位词,拆解滑动窗口结合哈希表的完整解题流程,区分两种窗口处理逻辑,附带可直接运行的 C++ 代码,帮读者吃透滑窗通用模板。


一、最大的连续1个数

1.1题目

链接:水果成蓝

1.2 算法原理

核心思想: 一段只包含两个数字的最长子串

1.2.1 算法思路

研究的对象是一段连续的区间,可以使用「滑动窗口」思想来解决问题。

让滑动窗口满足:窗口内水果的种类只有两种。

做法:

  • 右端水果进入窗口的时候,用哈希表统计这个水果的频次。这个水果进来后,判断哈希表的大小:
  • 如果大小超过 2:说明窗口内水果种类超过了两种。那么就从左侧开始依次将水果划出窗口,直到哈希表的大小小于等于 2,然后更新结果;
  • 如果没有超过 2,说明当前窗口内水果的种类不超过两种,直接更新结果 ret。

1.2.2 算法流程

a. 初始化哈希表 hash 来统计窗口内水果的种类和数量;

b. 初始化变量:左右指针 left = 0,right = 0,记录结果的变量 ret = 0;

c. 当 right 小于数组大小的时候,一直执行下列循环:

 i. 将当前水果放入哈希表中;

 ii. 判断当前水果进来后,哈希表的大小:

  ● 如果超过 2:

   ○ 将左侧元素滑出窗口,并且在哈希表中将该元素的频次减一;

   ○ 如果这个元素的频次减一之后变成了 0,就把该元素从哈希表中删除;

   ○ 重复上述两个过程,直到哈希表中的大小不超过 2;

 iii. 更新结果 ret;

 iv. right++,让下一个元素进入窗口;

d. 循环结束后,ret 存的就是最终结果。

1.3 代码

cpp 复制代码
class Solution {
public:
    int totalFruit(vector<int>& fruits) 
    {
        int map[100000] = {0};
        int kind = 0; //统计当前区间内水果的种类
        int l = 0,r = 0;
        int n = fruits.size();
        int ret = 0;
        while(r < n)
        {
            //进窗口
            if(map[fruits[r]]++ == 0)
                kind++;
            while(kind > 2) //窗口不合法 -- 水果种类大于2
            {
                if(map[fruits[l++]]-- == 1)
                    kind--;
            }
            ret = max(ret,r - l + 1);
            r++;
            
        }
        return ret;
    }
};

时间复杂度 : O(n)

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

2.1 题目

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

2.2 算法原理

核心思想滑动窗口 + 哈希表

2.2.1 算法思路

cpp 复制代码
异位词本质就是在一段区间内各个元素出现的次数和p字符串里各元素出现的次数相同

2.2.2 算法流程

2.2.2.1 法一

定义连个变量l,r来标识合法区间,定义两个哈希表,一个用来统计p串内各个元素出现的次数,另一个用来统计【l,r】区间内各个元素出现的次数并且和另一个哈希表做比较,看两个哈希表是否完全相同

2.2.2.2 法二

在法一的基础上定义个count来统计s中【l,r】有效字符个数的数量.最后和p元素长度作比较即可

有效字符个数,是在当前s串中的【l,r】区间中当该元素数量小于等于p串中该元素的数量则count加一,如果【l,r】是p的异位词则p的长度m必定和count相等。

2.3 代码

2.3.1 法一代码

cpp 复制代码
class Solution {
public:
    vector<int> findAnagrams(string s, string p) 
    {
        vector<int> ret;
        int hash1[26] = {0}; //统计p各元素出现次数
        int hash2[26] = {0}; //统计s各元素出现次数
        for(int i = 0;i < p.size();i++)
            hash1[p[i] - 'a']++;
        int l = 0,r = 0,n = s.size(),m = p.size();
        while(r < n)
        {
            //进窗口
            char ch = s[r];
            hash2[ch - 'a']++;

            if(r - l + 1 > m) //出窗口
                hash2[s[l++] - 'a']--;
            
            int f = 1;
            for(int i = 0;i < 26;i++)
            {
                if(hash1[i] != hash2[i])
                {
                    f = 0;
                    break;
                }
            }
            if(f)
                ret.push_back(l);
            r++;
        }
        return ret;
    }
};

2.3.2 法二代码

cpp 复制代码
class Solution {
public:
    vector<int> findAnagrams(string s, string p) 
    {
        vector<int> ret;
        int hash1[26] = {0}; //统计p各元素出现次数
        int hash2[26] = {0}; //统计s各元素出现次数
        for(int i = 0;i < p.size();i++)
            hash1[p[i] - 'a']++;
        int l = 0,r = 0,n = s.size(),m = p.size();
        int count = 0; //统计当前【l,r】区间内有效字符的个数
        while(r < n)
        {
            //进窗口
            char ch1 = s[r];
            if(++hash2[ch1 - 'a'] <= hash1[ch1 - 'a'])
                count++;
            if(r - l + 1 > m) //出窗口
            {
                char ch2 = s[l++];
                if(hash2[ch2 - 'a']-- <= hash1[ch2 - 'a'])
                    count--;
            }
            if(count == m)
                ret.push_back(l);          
            r++;
        }
        
        return ret;
    }
};

时间复杂度 : O(N)

总结与每日励

本文通过两道典型题目梳理滑动窗口核心逻辑,可变窗口动态收缩左边界,限制窗口内元素种类;定长窗口维持固定区间,搭配哈希统计字符频次。优化解法引入计数变量,省去逐一枚举 26 个字母比对,进一步简化逻辑。两类题目均仅一次遍历数组,时间复杂度稳定 O (n)。掌握窗口伸缩规则与哈希表状态维护,就能快速应对绝大多数滑动窗口题型。代码一行行敲,算法一道道啃,所有看似晦涩的逻辑,都会在反复练习中变得通透。不必畏惧刷题路上的卡顿与报错,每一次调试都是沉淀。沉下心深耕基础,稳步积累,那些默默付出的时光,终会化作面试与竞赛里稳稳的底气,永远相信美好的事情即将发生。

相关推荐
wWYy.1 小时前
数据结构:跳表
数据结构
自强的小白1 小时前
重点!起始时间和结束时间与创建时间和修改时间的区别(localdate与LocaldateTIME区别)以及增删改查什么时候设置目前时间(添加和更新员工时)
java
宇宙第一小趴菜1 小时前
六、Oracle 目录结构
数据库·oracle
雪的季节2 小时前
Python 线程同步与异步编程 全解析
java·开发语言
IvorySQL2 小时前
PG 日报|修复高 IO 并发场景,解决预读机制耗尽本地缓冲区缺陷
数据库·人工智能·sql·postgresql·区块链
TiDB 社区干货传送门2 小时前
TiDB 8.5.7 发版|六项实用功能上线
数据库·mysql·tidb
吃饱了得干活2 小时前
缓存与数据库一致性:从理论到实战
java·后端·面试
在繁华处2 小时前
MySQL 学习笔记 01:从概念到约束,搭好库表骨架
数据库·oracle
txzrxz2 小时前
数论:排列数、组合数、费马小定理、逆元、同余定理
c++·算法·数论·组合数·费马小定理·逆元·排列数