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

news2024/12/25 3:15:30

L

在 C++ 中,两个 vector<int> 类型的变量进行 == 操作时,会逐个比较它们的元素,只有当两个向量的长度相同且每个位置上的元素值都相同时,== 操作才会返回 true

因此,在这道题的代码中,sCount == pCount 这一操作会执行如下步骤:

  1. 长度比较:首先,两个 vector<int> 的长度会进行比较。如果长度不相等,直接返回 false
  2. 元素逐个比较:如果长度相等,那么会逐个比较两个向量对应位置的元素。如果每个位置上的元素值都相同,则返回 true;如果某个位置的元素不相等,则立即返回 false

在这道题中,sCountpCount 的长度始终固定为 26(对应 26 个小写字母),因此不会出现长度不同的情况。== 操作会逐个比较这 26 个元素的值,当 sCountpCount 相等时,意味着当前窗口中的字符频率与字符串 p 中字符的频率完全一致,即找到了一个异位词。

具体执行流程:

  • 对于每次 sCount == pCount,程序将比较两个 vector 的 26 个位置。
  • 如果所有位置的值都相同,说明这两个频率数组是一样的,那么就返回 true
  • 如果有一个位置的值不同,则返回 false

vector== 操作的时间复杂度:

在这个特定例子中,sCountpCount 的长度固定为 26,所以比较操作是常数时间 O(26),即 O(1)。

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        
        vector<int> result;
        //如果s的长度都小于p,那么没有可能的异位词,应该直接返回空结果。
        if (s.size() < p.size()) return result;

        //由于字符串仅仅包含小写字母,所以我们可以创建2个元素个数为26的向量来代替hash表
        //我们可以在这两个向量中统计p的字符频率和s中滑动窗口中的字符频率
        vector<int> pCount(26, 0);
        vector<int> sCount(26, 0);


        //然后分别统计p中的字符频率和第一个滑动窗口中的字符频率
        for(char c:p) {
            pCount[c - 'a']++;
        }

        int windowSize = p.size();

        for(int i = 0; i < windowSize; ++i) {
            sCount[s[i] - 'a']++;
        }

        //如果p的字符频率和第一个窗口中的字符频率相同,则将下标0加入到result数组中。
        if(pCount == sCount) { //当且仅当两个向量中每个位置的每个元素都相同时, == 返回true
            result.push_back(0);
        }

        //然后开始滑动窗口, 这里 i 是滑动窗口的最右下标
        for(int i = windowSize; i < s.size(); ++i) {
            sCount[s[i] - 'a']++;
            sCount[s[i - windowSize] - 'a']--; //之前窗口最左边的元素删除
            if(sCount == pCount) {
                // i - windowSize + 1 是窗口起始下标,
                //因为 i - 起始下标 + 1 = windowSize; 所以窗口起始下标 = i - windowSize + 1
                result.push_back(i - windowSize + 1); 
            }
        }
    return result;
    }
};

算法代码中,最巧妙的地方是通过这两行代码,将字符串p和窗口中的子串每个字符存储在下标相同的数组位置上,这使得可以通过==操作符来判断是否是异位词

pCount[c - 'a']++;

sCount[s[i] - 'a']++;

是的,你理解得非常准确!这两行代码是该算法的关键部分之一,使用了字符的 ASCII 码来将字符映射到数组的正确位置上。

具体来说:

  • pCount[c - 'a']++ 中,c - 'a' 计算的是字符 c 到字符 'a' 的偏移量。由于小写字母的 ASCII 码是连续的(‘a’ 的 ASCII 码最小,其他字母依次递增),所以通过 c - 'a' 可以将字母 'a' 对应到数组的索引 0,字母 'b' 对应到索引 1,依次类推直到 'z' 对应到索引 25。
  • pCountsCount 都是长度为 26 的数组,每个元素表示相应字母的出现频率。例如,pCount[0] 代表字符 'a' 在字符串 p 中出现的次数,pCount[1] 代表字符 'b' 的出现次数,依次类推。
  • 对于 sCount[s[i] - 'a']++,它将字符串 s 中当前窗口的字符同样映射到对应的数组位置,统计每个字符在窗口内出现的次数。

巧妙之处:

  1. 数组映射代替哈希表:因为只有 26 个小写字母,所以我们用一个长度为 26 的数组(vector<int>)来代替哈希表,这样避免了复杂的哈希映射,同时通过数组的下标操作可以快速访问字符对应的位置,节省了时间。

  2. 字符频率比较:通过将两个字符串的字符频率存储在数组中的对应位置(下标),我们可以直接利用 == 操作符逐个比较两个频率数组,判断当前窗口中的子串是否是 p 的异位词。这种方式比使用排序或者逐个字符比较更加高效,特别适合字符种类较少的情况。

总结:

这两行代码的巧妙之处在于将字符直接映射到数组下标,从而可以用数组来记录字符出现的频率,并通过简单的数组比较来判断异位词,从而使得算法简洁、高效。

为什么这一行代码的下标是i - windowSize + 1而不是i - windowSize?

            if(sCount == pCount) {
                // i - windowSize + 1 是窗口起始下标,
                //因为 i - 起始下标 + 1 = windowSize; 所以窗口起始下标 = i - windowSize + 1
                result.push_back(i - windowSize + 1); 
            }

这行代码中的下标 i - windowSize + 1 是用来计算异位词的起始位置。让我们一步步来理解为什么这个下标需要加 1

滑动窗口的工作原理:

假设 p 的长度是 windowSize,每次滑动窗口检查的都是一个长度为 windowSize 的子串。

  • i 为当前窗口的最右端下标时,窗口的左端下标就是 i - windowSize + 1,这样窗口的长度恰好为 windowSize

举个例子:

假设字符串 s = "abab", p = "ab",则 windowSize = 2,我们看看当 i 变化时,窗口的边界情况:

  • i = 1 时,当前窗口的字符是 "ab"s[0]s[1]),它是从 i - windowSize + 1 = 1 - 2 + 1 = 0 开始的。
  • i = 2 时,当前窗口的字符是 "ba"s[1]s[2]),它是从 i - windowSize + 1 = 2 - 2 + 1 = 1 开始的。

可以看到,加上 +1 是为了得到窗口的起始位置,因为 i 是当前窗口的最右端索引,而我们需要将这个窗口的最左端索引加入到结果中。

更详细的解释:

  • i 是当前窗口的 最右端 的索引(即当前我们处理的字符的索引)。
  • windowSize 是窗口的长度,所以窗口的左端应该是 i - windowSize + 1
  • 如果我们直接用 i - windowSize,得到的是窗口左端 之前 的一个位置,不是实际的起始位置。因此,需要加 1 来得到准确的左端起始位置。

总结:

i - windowSize + 1 的作用是从当前窗口的最右端 i 推导出该窗口的 左端起始位置,并将这个起始位置存入结果中。这是因为 i 对应的是当前滑动窗口的最右边界,而我们需要的是这个窗口的最左边界(即异位词的起始位置)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2141348.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flip动画的实现示例demo

Flip动画的实现示例demo 文章说明核心代码效果展示Flip动画工具类的封装 文章说明 文章主要为了学习flip动画的实现思路&#xff0c;并且采用此示例效果来理解该实现思路的含义 参考渡一前端袁老师的讲解视频 核心代码 采用简单的y轴变化的动画效果为示例 <!DOCTYPE html>…

【K230 实战项目】气象时钟

【CanMV K230 AI视觉】 气象时钟 功能描述&#xff1a;说明HMDI资源3.5寸屏幕 使用方法 为了方便小伙伴们理解&#xff0c;请查看视频 B站连接 功能描述&#xff1a; 天气信息获取&#xff1a;通过连接到互联网&#xff0c;实时获取天气数据&#xff0c;包括温度、湿度、天气状…

【STM32】独立看门狗(IWDG)原理详解及编程实践(上)

本篇文章是对STM32单片机“独立看门狗&#xff08;IWDG&#xff09;”的原理进行讲解。希望我的分享对你有所帮助&#xff01; 目录 一、什么是独立看门狗 &#xff08;一&#xff09;简介 &#xff08;二&#xff09;、独立看门狗的原理 &#xff08;三&#xff09;、具体操…

vulkano (rust) 画一个三角形 (vulkan 渲染窗口初始化 (Linux) 下篇)

上文说到, vulkan 相比 OpenGL (ES), 更加贴近底层硬件, 许多东西需要应用软件手动管理, 所以 vulkan 的初始化过程比较麻烦, 或者说学习曲线比较陡峭. 但是, 这种麻烦是一次性的, 一旦学会了, 就能开始享受 vulkan 的诸多好处啦 ~ 本文以绘制一个三角形为例, 介绍 vulkan 的初…

2024最新版,人大赵鑫老师《大语言模型》新书pdf分享

本书主要面向希望系统学习大语言模型技术的读者&#xff0c;将重点突出核心概念与 算法&#xff0c;并且配以示例与代码&#xff08;伪代码&#xff09;帮助读者理解特定算法的实现逻辑。由于大语言模型技术的快速更迭&#xff0c;本书无法覆盖所有相关内容&#xff0c;旨在梳理…

瓶中水位检测系统源码分享

瓶中水位检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

【数据结构】图的概念和存储结构

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《C游记》《进击的C》《Linux迷航》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、图的概念二、图的存储结构2.1 邻接矩阵2.1.1 成员变量与默认成员函数2.1.2 GetIndex2.1.3 AddEdge2.1.4 Pr…

使用 Java 初步搭建简单Spring 项目框架:

一、准备工作 安装 Java Development Kit (JDK)&#xff0c;确保环境变量配置正确。 安装一个集成开发环境&#xff08;IDE&#xff09;&#xff0c;如 IntelliJ IDEA 或 Eclipse。 二、创建项目——具体步骤 在 ider 中创建一个新的 Maven 项目 使用 Maven&#xff1a; 在…

Linux bash脚本本地开发环境(Git Bash)配置

参考资料 VSCode: Windows 下配置 VSCode运行shellVSCodeを使用したシェルスクリプトの開発環境作成 目录 一. 必备VSCode插件二. 插件配置说明2.1 Bash IDE2.2 Code Runner2.3 shell-format 一. 必备VSCode插件 Bash IDE 该插件为 Bash 脚本提供了一些实用的开发工具和功能&…

鸿蒙 ArkUI组件二

ArkUI组件&#xff08;续&#xff09; 文本组件 在HarmonyOS中&#xff0c;Text/Span组件是文本控件中的一个关键部分。Text控件可以用来显示文本内容&#xff0c;而Span只能作为Text组件的子组件显示文本内容。 Text/Span组件的用法非常简单和直观。我们可以通过Text组件来显…

重生归来之挖掘stm32底层知识(1)——寄存器

概念理解 要使用stm32首先要知道什么是引脚和寄存器。 如下图所示&#xff0c;芯片通过这些金属丝与电路板连接&#xff0c;这些金属丝叫做引脚。一般做软件开发是不需要了解芯片是怎么焊的&#xff0c;只要会使用就行。我们平常通过编程来控制这些引脚的输入和输出&#xff0c…

应用软件系统开发实操二:任务需求描述

工信部软件界信息技术服务业2020、2021、2022年度数据&#xff08;目前只有这3年的完整数据&#xff09;&#xff0c;以SQL的格式&#xff0c;存放在实操平台上&#xff0c;通过浏览器下载的方式获取。获取数据后&#xff0c;采用自己选择的技术对数据进行处理。阅读下面的要求…

php语言基本语法

HP&#xff08;Hypertext Preprocessor&#xff09;是一种广泛使用的开源服务器端脚本语言&#xff0c;特别适合于Web开发。 它能够嵌入到HTML中&#xff0c;执行动态网页内容。 PHP的一些基本语法元素&#xff1a; 1. 基本结构 PHP代码通常嵌入到HTML中&#xff0c;以<…

C/C++实现植物大战僵尸(PVZ)(打地鼠版)

&#x1f680;欢迎互三&#x1f449;&#xff1a;程序猿方梓燚 &#x1f48e;&#x1f48e; &#x1f680;关注博主&#xff0c;后期持续更新系列文章 &#x1f680;如果有错误感谢请大家批评指出&#xff0c;及时修改 &#x1f680;感谢大家点赞&#x1f44d;收藏⭐评论✍ 游戏…

Django_Vue3_ElementUI_Release_003_前端Vue3项目初始化

1. 概念扫盲 Node.js是基于ChromeV8引擎&#xff0c;让JS在服务端运行的开发平台&#xff0c;就是JS的一种解释器WebPack就是模块打包机&#xff0c;把浏览器不能直接运行的拓展语言找到并打包为合适的格式给浏览器直接使用Vue基于WebPack构件项目的&#xff0c;并带有合理默认…

MoCo对比损失

MoCo&#xff08;Momentum Contrast&#xff0c;动量对比学习&#xff09;是一种自监督学习方法&#xff0c;由Facebook AI Research提出&#xff0c;主要用于无监督学习视觉表示。在MoCo中&#xff0c;对比损失&#xff08;Contrastive Loss&#xff09;扮演着至关重要的角色&…

在麒麟操作系统中查看进程运行时间

在麒麟操作系统中查看进程运行时间 1、使用ps命令查看进程运行时间1.1 基本命令结构1.2 示例&#xff1a;查看sshd进程的运行时间 2、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Linux操作系统中&#xff0c;包括麒麟&#xff08…

使用Mockito进行单元测试

1、单元测试介绍 Mockito和Junit是用于单元测试的常用框架。单元测试即&#xff1a;从最小的可测试单元&#xff08;如函数、方法或类&#xff09;开始&#xff0c;确保每个单元都能按预期工作。单元测试是白盒测试的核心部分&#xff0c;它有助于发现单元内部的错误。 单元测试…

【Hot100】LeetCode—84. 柱状图中最大的矩形

目录 1- 思路题目识别单调栈 2- 实现⭐84. 柱状图中最大的矩形——题解思路 3- ACM 实现 原题链接&#xff1a;84. 柱状图中最大的矩形 1- 思路 题目识别 识别1 &#xff1a;给定一个数组 heights &#xff0c;求解柱状图的最大面积 单调栈 使用 Stack 来实现&#xff0c;遍…

服务器上PFC配置丢失问题排查与解决方案

现象 基于nccl的多轨通信算力中心出现交换机端口出入方向丢包 分析 机间通信使用RoCE网络&#xff0c;为了避免因丢包导致大量重传报文影响训练性能&#xff0c;我们基于PFC和ECN在交换机和服务器配置搭建了无损网络&#xff0c;理论上是不允许丢包的&#xff0c;现在出现交…