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

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.mfbz.cn/a/878710.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux篇】TCP/IP协议(笔记)

目录 一、TCP/IP协议族体系结构 1. 数据链路层 &#xff08;1&#xff09;介绍 &#xff08;2&#xff09;常用协议 ① ARP协议&#xff08;Address Resolve Protocol&#xff0c;地址解析协议&#xff09; ② RARP协议&#xff08;Reverse Address Resolve Protocol&…

基于对数变换的图像美白增强,Matlab实现

博主简介&#xff1a;matlab图像处理&#xff08;QQ:3249726188&#xff09; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 本次案例是基于对数变换的图像美白增强&#xff0c;用matlab实现。 一、案例背景和算法介绍 这次案例是美白算法&…

JavaScript 事件处理

一、简介 ​ 事件&#xff1a;发生在HTML元素上的事情&#xff0c;可以是用户的行为&#xff0c;也可以是浏览器的行为&#xff0c;如 用户点击了某个HTML元素用户将鼠标移动到某个HTML元素上用户输入数据时光标离开页面加载完成 ​ 事件源&#xff1a;事件触发的源头&#xf…

知识|智能网联汽车多域电子电气架构会如何发展?

摘要&#xff1a;随着汽车智能化和网联化技术的快速发展&#xff0c;传统的电子电气架构已经无法满足未来车路云网一体化发展的新需求。本文聚焦于未来智能网联汽车的多域电子电气架构&#xff0c;并从总体设计、硬件系统、通信系统和软件系统四个方面对现有技术进行了详细的综…

STL-vector练习题

118. 杨辉三角 思路&#xff1a; 杨辉三角有以下性质使我们要用到的&#xff1a; ● 每行数字左右对称&#xff0c;由 1 开始逐渐变大再变小&#xff0c;并最终回到 1。 ● 第 n 行&#xff08;从 0 开始编号&#xff09;的数字有 n1 项&#xff0c;前 n 行共有 2n(n1)个数。…

使用ShardingSphere实现MySql的分库分表

目录 一 什么是ShardingSphere分库分表 二 代码实现 1.导入相关依赖 2.配置相关参数 3.创建学生类以及mapper接口 4.实现 StandardShardingAlgorithm接口自定义分片算法 唐洋洋我知道你在看!!!嘿嘿 一 什么是ShardingSphere分库分表 我们平时在设计数据库的时候&#xf…

基于UDP的简易网络通信程序

目录 0.前言 1.前置知识 网络通信的大致流程 IP地址 端口号&#xff08;port&#xff09; 客户端如何得知服务器端的IP地址和端口号&#xff1f; 服务器端如何得知客户端的IP地址和端口号&#xff1f; 2.实现代码 代码模块的设计 服务器端代码 成员说明 成员实现 U…

2024 年 GitLab Global DevSecOps 报告解读

近日 GitLab 正式发布了 2024 年 GitLab Global DevSecOps 报告&#xff0c;报告主题为 What’s next in DevSecOps。在全球有超 5000 位 IT 人员参与了该报告的调研&#xff0c;超 70% 为企业管理者&#xff0c;50% 以上的受访者所在企业规模超过 500人。该报告深刻揭示了在 A…

Andrej Karpathy谈AI未来:自动驾驶、Transformer与人机融合

引言 在人工智能领域&#xff0c;Andrej Karpathy 是一个无法忽视的名字。从他早期在 OpenAI 的工作&#xff0c;到后来担任 Tesla 的 AI 主管&#xff0c;他在自动驾驶、深度学习等方面的贡献广为人知。最近&#xff0c;卡帕西做客了著名的播客节目 No Priors&#xff0c;他在…

鸿蒙开发基础

页面跳转 了解代码初始结构 /*** 装饰器&#xff1a;用于装饰类、结构、方法以及变量&#xff0c;并赋予其特殊的含义。* Entry&#xff1a;表示该自定义组件为入口组件 * Component&#xff1a;表示自定义组件* State&#xff1a;表示组件中的状态变量&#xff0c;状态变量变…

hh exe所选的程序不能与此文件类型相关联。请选择其他程序。

按照hh exe打开chm文件显示所选的程序不能与此文件类型相关联。请选择其他程序。 以上错误来自于 cmd命令行 cd C:\Windows\hh.exe 要打开的chm文件报错 其实根本原因是在设置中.chm文件默认打开方法被其他软件占用了&#xff0c;解决办法只能删除那个软件&#xff0c;如果是W…

接口测试(十二)

一、前台、后台、数据库三者关系 fiddler抓包是抓取客户端 --> 服务端 发送的的请求接口 开N个网页&#xff0c;只要有对后端发送请求&#xff0c; fiddler是无差别抓取 F12只抓取当前页面的数据 二、接口概念 接口是什么&#xff1f;— 传递数据的通道 测试系统组件间接口…

五、(JS)window中的定时器

一、为什么叫做window中的定时器 我们在全局中会用到一些函数&#xff0c;比如说alert函数&#xff0c;prompt函数&#xff0c;setTimeout等等 我们有在这里定义过这些函数吗&#xff1f;很明显没有。可见我们这些函数都是来自于window。 所以还可以写成window.setTimeout。…

AtCoder Beginner Contest 371

A - Jiro &#xff1a; 题目&#xff1a; 代码&#xff1a; #include <bits/stdc.h>using namespace std;typedef long long LL ; typedef pair<int,int> PII;void solve() {string a,b, c;cin>>a>>b>>c;string s(3,a);s[0]a[0];s[1]b[0];s[2…

Java集合(八股)

这里写目录标题 Collection 接口List 接口ArrayList 简述 1. ArrayList 和 LinkedList 区别&#xff1f;⭐️⭐️⭐️⭐️2. ArrayList 和 Array 的区别&#xff1f;⭐️⭐️⭐️ArrayList 和 Vector 区别&#xff1f;⭐️⭐️ArrayList 的扩容机制&#xff1f;⭐️⭐️⭐️ Qu…

18063 圈中的游戏

### 思路 1. 创建一个循环链表表示围成一圈的 n 个人。 2. 从第一个人开始报数&#xff0c;每报到 3 的人退出圈子。 3. 重复上述过程&#xff0c;直到只剩下一个人。 4. 输出最后留下的人的编号。 ### 伪代码 1. 创建一个循环链表&#xff0c;节点表示每个人的编号。 2. 初始…

Vue3+TS项目封装一个公共的el-table组件二次封装

前言 支持动态传入列&#xff0c;列内容可以指定插槽&#xff0c;指定格式化显示 样式没太写&#xff0c;主要分享基础功能封装 效果 Table组件代码BaseTable.vue <template><el-table :data"data" border><template v-for"col in columns&q…

通过防火墙分段增强网络安全

什么是网络分段‌ 随着组织规模的扩大&#xff0c;管理一个不断扩大的网络成为一件棘手的事情&#xff0c;同时确保安全性、合规性、性能和不间断的运行可能是一项艰巨的任务。为了克服这一挑战&#xff0c;网络管理员部署了网络分段&#xff0c;这是一种将网络划分为更小且易…

react18基础教程系列-- 框架基础理论知识mvc/jsx/createRoot

react的设计模式 React 是 mvc 体系&#xff0c;vue 是 mvvm 体系 mvc: model(数据)-view(视图)-controller(控制器) 我们需要按照专业的语法去构建 app 页面&#xff0c;react 使用的是 jsx 语法构建数据层&#xff0c;需要动态处理的的数据都要数据层支持控制层: 当我们需要…

YoloV8 trick讲解

1.将 YOLOv5 的 C3结构换成了梯度流更丰富的 C2f结构: C3 C3 模块的设计灵感来自 CSPNet&#xff0c;其核心思想是将特征图的部分通道进行分割和并行处理&#xff0c;目的是减少冗余梯度信息&#xff0c;同时保持较高的网络表达能力。C3 结构与传统的残差结构类似&#xff0c;但…