每日两道力扣,day8

每日两道力扣,day8

每日两道力扣,day8

每日两道力扣,今日是:

3. 无重复字符的最长子串 - 力扣(LeetCode)

438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

第一题:无重复字符的最长子串

3. 无重复字符的最长子串 - 力扣(LeetCode)

1.思路:

首先s由英文字母、数字、符号和空格组成,所以我们可以知道总共有128个字符,咱们定义一个int类型的hash数组,数组大小为128,初始化数组为0。

看到这种跟子串有关的题目,我们应该条件反射,立马想到滑动窗口算法,我称之毛毛虫算法。像一条毛毛虫一样,先蠕动右边,到达零界点,再移动左边。遍历完数组后,得到答案。

因为我们已经定义了一个hash数组用来记录窗口内每个字符的出现次数,剩下的只需要按进窗口------出窗口------更新结果这"三部曲"走。

具体实现见代码。

2.代码实现:
复制代码
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        
        int hash[128] = {0};
        int left = 0, right = 0;
        int len = 0;
        //进窗口
        while(right < s.size())
        {
            char in = s[right];
            hash[in]++;
            
            //出窗口
            while(hash[in] > 1)
            {
                char out = s[left];
                hash[out]--;
                left++;
            }

            //更新结果
            len = max(len,right - left + 1);
            right++;
        }
        return len;
    }
};
3.细节:

在这类题中,尤其要注意进窗口,出窗口,更新结果的时机。

第二题:找到字符串所以字母的异位词

找到字符串所以字母的异位词


1.思路:

一、 核心解题思路:定长滑动窗口 + 极速校验

这道题的本质是:在一个长字符串 s 中,拿一个固定大小的框(长度等于 p 的长度),从左到右去套,看框住的字符是不是和 p 的字符构成完全一样。

(1)制定"通关配额"(哈希表统计): 因为全是小写字母,我们直接开一个大小为 26 的数组作为哈希表,先统计目标字符串 p 中每个字符需要的数量。这就是我们的"通关配额"。

(2)引入灵魂变量 count(有效匹配数): 如果我们每次框住一段字符,都去循环遍历比对两个哈希表是不是长得一样,那就太笨重了。 神级操作是引入一个变量 count,专门记录=="当前框里,有几个字符是有效且被需要的==。 只要 count 的数量正好等于 p 的长度,说明框里全是"宝",且数量不多不少刚刚好,直接收集答案!

(3)右边吃,左边拉(滑动窗口运转):

  • 进窗口(吃): 右指针往右走,吃进一个字符。如果这个字符在框里的数量 小于等于 配额里的数量,说明它是"被需要的",count 增加。
  • 出窗口(拉): 当框的长度超过了 p 的长度,左指针必须往右走,吐出一个字符。如果吐出前,这个字符在框里的数量 小于等于 配额数量,说明我们"吐掉了一块宝",count 减少。
  • 收集答案: 每次出入操作完成后,看一眼 count 是否等于 p 的长度,等于就记录左指针的位置。
2.代码实现:
复制代码
class Solution 
{
 public:
    vector<int> findAnagrams(string s, string p) 
    {
        vector<int> ret;
        int hash1[26] = { 0 }; // 统计字符串 p 中每个字符出现的个数
        for(auto ch : p) hash1[ch - 'a']++;
        int hash2[26] = { 0 }; // 统计窗⼝⾥⾯的每⼀个字符出现的个数
        int m = p.size();
        for(int left = 0, right = 0, count = 0; right < s.size(); right++)
        {
            char in = s[right];
            // 进窗⼝ + 维护 count
            if(++hash2[in - 'a'] <= hash1[in - 'a']) count++; 
            if(right - left + 1 > m) // 判断
            {
                char out = s[left++];
                // 出窗⼝ + 维护 count
                if(hash2[out - 'a']-- <= hash1[out - 'a']) count--; 
            }
            // 更新结果
            if(count == m) ret.push_back(left);
        }
        return ret;
    }
 };
3.细节:

在手撕代码时,这几个细节极其容易让程序崩溃或报错:

1. 字符映射到数组下标(越界警告):

千万不要直接拿字符去当数组下标!题目说了是纯小写字母,一定要用 当前字符 - 'a' 来映射到 0 到 25 的索引上。如果不减去 'a',直接用 ASCII 码访问大小只有 26 的数组,会瞬间内存越界。

2. count 增减的严密逻辑(不要乱加减):

  • 进窗口时: 把框里的字符计数 +1, 和配额比较。如果 ≤ \le ≤ 配额,才算有效,count++。如果是超额的废料,框里计数增加,但 count 绝不能加。

  • 出窗口时: 把当前的框里计数和配额比较,如果是 ≤ \le ≤ 配额,说明要掉肉了,count--。比较完之后,把框里的字符计数 -1。

    (这也就是为什么优秀的代码里会用 ++hash[in] <= targethash[out]-- <= target 这种前置和后置的写法,顺序绝不能错!)

3. 窗口大小的严格控制:

因为寻找的是异位词,异位词的长度绝对不可能变。所以维护窗口大小时,判断条件是 if (当前窗口长度 > p的长度),只要大于,就无条件触发左指针的"出窗口"动作,确保窗口像一个硬纸板框一样,长度永远不变地往右平移。

4. 初始状态的考量:

如果 s 的长度比 p 还要短,那连一个框都塞不下,根本不可能有异位词。虽然现在的滑动窗口代码在逻辑上能兼容这种情况,但在追求极致的面试中,可以在函数开头加上一句 if (s.size() < p.size()) return {};,能省去不必要的运行开销。

好了,今天的每日两道力扣到这里就算是结束了,看完是不是感觉有所收获呢?如果学有所获的话,麻烦给个三连支持一下呗。感谢观看,您的支持,将是我前进路上的重要动力。

相关推荐
王老师青少年编程32 分钟前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·合并果子
叼烟扛炮1 小时前
C++第二讲:类和对象(上)
数据结构·c++·算法·类和对象·struct·实例化
天疆说1 小时前
【哈密顿力学】深入解读航天器交会最优控制中的Hamilton函数
人工智能·算法·机器学习
wuweijianlove2 小时前
关于算法设计中的代价函数优化与约束求解的技术7
算法
leoufung2 小时前
LeetCode 149: Max Points on a Line - 解题思路详解
算法·leetcode·职场和发展
样例过了就是过了2 小时前
LeetCode热题100 最长公共子序列
c++·算法·leetcode·动态规划
HXDGCL3 小时前
矩形环形导轨:自动化循环线的核心运动单元解析
运维·算法·自动化
谭欣辰3 小时前
C++ 排列组合完整指南
开发语言·c++·算法
代码中介商3 小时前
银行管理系统的业务血肉 —— 流程、状态机、输入校验与持久化(下篇)
c语言·算法
橙子也要努力变强3 小时前
信号捕捉底层机制-机理篇2
linux·服务器·c++