L51.【LeetCode题解】438. 找到字符串中所有字母异位词(四种方法)

目录

1.题目

2.分析

暴力解法

方法1:排序(超时)

方法2:哈希表(险过)

★判断两个哈希表是否相同算法(通用方法,必须掌握)

能相等的前提:两个哈希表的大小相等

哈希表有迭代器,可以使用范围for从头到尾遍历

提交结果

优化方法:定长滑动窗口

提交结果

使用哈希数组更快

提交结果

★★★更优化的方法:不定长滑动窗口(比定长的要快!)

提交结果


1.题目

https://leetcode.cn/problems/find-all-anagrams-in-a-string/

给定两个字符串 s 和 p,找到 s 中所有 p 的 

 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

示例 1:

输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。

 示例 2:

输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

提示:

  • 1 <= s.length, p.length <= 3 * 104
  • s 和 p 仅包含小写字母

2.分析

暴力解法

大致思路:使用i遍历字符串s,从i位置截取和p相同长度的子串(前提p.size()<=s.size(),这个要在一开始就要判断),比较这个子串和p是否是异位词

方法1:排序(超时)

可以先对两个串排序,再调用operator==判断是否相等

class Solution {
public:vector<int> findAnagrams(string s, string p){if (p.size()>s.size())return {};vector<int> ret;sort(p.begin(),p.end());for (int i = 0; i <= s.size() - p.size(); i++){string tmp=s.substr(i,p.size());sort(tmp.begin(),tmp.end());if (p==tmp)ret.push_back(i);}return ret;}
};

调用sort函数的时间复杂度是O(NlogN),过高容易超时:

方法2:哈希表(险过)

统计s的子串和p串的词频,记录到哈希表中,再判断两个哈希表是否相同,这个时间复杂度比方法1低一点

★判断两个哈希表是否相同算法(通用方法,必须掌握)

虽然可以用哈希数组hash['z'+1]投机取巧(这个解法在本文的最后),但是其他题可能用不了哈希数组,因此掌握通用的方法是很有必要的

算法:

能相等的前提:两个哈希表的大小相等
if (mp1.size() != mp2.size()) return false;
哈希表有迭代器,可以使用范围for从头到尾遍历

STL的map底层实现是红黑树,对于for (const auto& pair : mp1),pair拿到的是mp1的红黑树中一个节点,到mp2中查有没有相同的节点即可,使用find函数

find函数的查找逻辑:

cplusplus网上是这样说的:https://legacy.cplusplus.com/reference/map/map/find/

find取得(get)指向元素的迭代器(iterator to element)

在容器中查找一个键值与k等价的元素,如果找到,则返回指向该元素的迭代器;否则返回指向map::end的迭代器

比较时会出现两种情况

1.找到一个键值与k等价的元素,此时还不能断定两个节点一定相等,需要比较第二个关键字(pair.second)是否相等,如果不等(it->second != pair.second),返回false

2.没找到一个键值与k等价的元素(it == mp2.end()),返回false

class Solution {
public:bool check(map<int,int>& mp1,map<int,int>& mp2){if (mp1.size() != mp2.size()) return false;for (const auto& pair : mp1) {auto it = mp2.find(pair.first);if (it == mp2.end() || it->second != pair.second) return false;}return true;}vector<int> findAnagrams(string s, string p){if (p.size()>s.size())return {};vector<int> ret;map<int,int> mp1;for (int i=0;i<p.size();i++)mp1[p[i]]++;for (int i = 0; i <= s.size() - p.size(); i++){map<int,int> mp2;string tmp=s.substr(i,p.size());for (int i=0;i<tmp.size();i++)mp2[tmp[i]]++;if (check(mp1,mp2))ret.push_back(i);}return ret;}
};
提交结果

优化方法:定长滑动窗口

以s = "cbaebabacd", p = "abc"为例分析:

异位词子串首先要长度和p从串相等,对s从头到尾遍历即可,

在移动窗口时,注意左侧元素离开哈希表,右侧元素加入哈希表,当mp2[s[left]]==0时,必须删除这个节点,否则影响哈希表的结构

class Solution {
public:bool check(map<int,int>& mp1,map<int,int>& mp2){if (mp1.size() != mp2.size()) return false;for (const auto& pair : mp1) {auto it = mp2.find(pair.first);if (it == mp2.end() || it->second != pair.second) return false;}return true;}vector<int> findAnagrams(string s, string p){if (p.size()>s.size())return {};vector<int> ret;map<int,int> mp1,mp2;for (int i=0;i<p.size();i++){mp1[p[i]]++;mp2[s[i]]++;}for (int left=0,right=p.size()-1; right<s.size();left++,right++){if (check(mp1,mp2)){ret.push_back(left);}mp2[s[left]]--;mp2[s[right+1]]++;if (mp2[s[left]]==0)mp2.erase(s[left]);}return ret;}
};

注:right+1最大为s.size(),此时mp2[s[right+1]]++;访问到字符串的'\0'没有越界,不影响结果 

提交结果

使用哈希数组更快

将map<int,int>改成数组,其他地方稍作修改即可

class Solution {
public:bool check(int* mp1,int* mp2){for(int i='a';i<='z';i++)if (mp1[i]!=mp2[i])return false;return true; }vector<int> findAnagrams(string s, string p){if (p.size()>s.size())return {};vector<int> ret;int mp1['z'+1]={0},mp2['z'+1]={0};for (int i=0;i<p.size();i++){mp1[p[i]]++;mp2[s[i]]++;}for (int left=0,right=p.size()-1; right<s.size();left++,right++){if (check(mp1,mp2))ret.push_back(left);mp2[s[left]]--;mp2[s[right+1]]++;}return ret;}
};
提交结果

★★★更优化的方法:不定长滑动窗口(比定长的要快!)

hash_s是字符串s的滑动窗口的哈希数组,hash_p是字符串p的的哈希数组

上面的优化的方法还可以继续优化,引入有效字符的个数:

先让hash_s[s[right]]++,之后判断:

1.当hash_s[s[right]] <= hash_p[s[right]]时计入有效字符的个数count,即count++

2.一旦窗口长度大于len时,及时调整让left++,当hash_s[s[left]] <= hash_p[s[left]]时减小有效字符的个数count,即count--

★更新结果的条件:窗口长度相等,且有效字符的个数要相等,这样就不用像上面方法那样遍历mp1和mp2数组的每个元素

个数从0变成1,有效字符的个数+1,因此count++

个数从1变成0,有效字符的个数-1,因此count--

符合有效字符的个数==p串的长度,将left尾插到返回数组ret

class Solution {
public:vector<int> findAnagrams(string s, string p) {if (p.size() > s.size())return {};vector<int> ret;int len = p.size();int count = 0;int hash_s['z'+1] = {0}, hash_p['z'+1] = {0};for (int i = 0; i < p.size(); i++)hash_p[p[i]]++;for (int left = 0, right = 0; right < s.size(); right++) {hash_s[s[right]]++;if (hash_s[s[right]] <= hash_p[s[right]])count++;if (right - left + 1 > len) {if (hash_s[s[left]] <= hash_p[s[left]])count--;hash_s[s[left]]--;left++;}if (count == len)ret.push_back(left);}return ret;}
};
提交结果

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

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

相关文章

Qt模块化架构设计教程 -- 轻松上手插件开发

概述 在软件开发领域,随着项目的增长和需求的变化,保持代码的可维护性和扩展性变得尤为重要。一个有效的解决方案是采用模块化架构,尤其是利用插件系统来增强应用的功能性和灵活性。Qt框架提供了一套强大的插件机制,可以帮助开发者轻松实现这种架构。 模块化与插件系统 模…

深入理解 HashMap 的索引计算:右移与异或的作用

在 Java 中&#xff0c;HashMap 是一种高效的数据结构&#xff0c;它通过将键映射到数组中的索引位置来实现快速的插入和查找。但之前看源码总是理解到它要hash之后散列到数组中某一个位置&#xff0c;但却从未深究它究竟怎么散列的&#xff0c;如果不够散那就意味着hash冲突增…

overleaf较高级的细节指令

换行命令 原来代码是将三个矩阵表达式在同一行显示&#xff0c;使用aligned环境&#xff08;需引入amsmath宏包&#xff0c;一般文档导言区默认会引入&#xff09;&#xff0c;把三个矩阵的定义分别放在不同行&#xff0c;可通过\\换行。 对齐命令 &放在等号前&#xff0…

LiteLLM:统一API接口,让多种LLM模型调用如臂使指

在人工智能迅猛发展的今天,各种大语言模型(LLM)层出不穷。对开发者而言,如何高效集成和管理这些模型成为一个棘手问题。LiteLLM应运而生,它提供了一个统一的API接口,让开发者可以轻松调用包括OpenAI、Anthropic、Cohere等在内的多种LLM模型。本文将深入介绍LiteLLM的特性、…

Google语法整理

以下是从整理出的 Google 语法&#xff1a; site&#xff1a;指定域名&#xff0c;如 “apache site:bbs.xuegod.cn”&#xff0c;可查询网站的收录情况 。 inurl&#xff1a;限定在 url 中搜索&#xff0c;如 “inurl:qq.txt”&#xff0c;可搜索 url 中包含特定内容的页面&a…

python 写一个工作 简单 番茄钟

1、图 2、需求 番茄钟&#xff08;Pomodoro Technique&#xff09;是一种时间管理方法&#xff0c;由弗朗西斯科西里洛&#xff08;Francesco Cirillo&#xff09;在 20 世纪 80 年代创立。“Pomodoro”在意大利语中意为“番茄”&#xff0c;这个名字来源于西里洛最初使用的一个…

Compose Multiplatform iOS 稳定版发布:可用于生产环境,并支持 hotload

随着 Compose Multiplatform 1.8.0 的发布&#xff0c;iOS 版本也引来的第一个稳定版本&#xff0c;按照官方的原话&#xff1a;「iOS Is Stable and Production-Ready」 &#xff0c;而 1.8.0 版本&#xff0c;也让 Kotlin 和 Compose 在移动端有了完整的支持。 在 2023 年 4 …

Jenkins 服务器上安装 Git

安装 Git # 更新包列表 sudo apt update# 安装 Git sudo apt install git 验证安装 # 检查 Git 版本 git --version 查看所有全局配置 git config --global --list 查看特定配置项 # 查看用户名配置 git config --global user.name# 查看邮箱配置 git config --global u…

OpenHarmony SystemUI开发——实现全局导航栏和状态栏关闭

在实际生产中&#xff0c;进场遇到需要关闭导航栏和状态栏的需求&#xff0c;现分享解决办法&#xff1a; 开发环境 OpenHarmony 5.0.0r 代码分析 思路&#xff1a; launcher本身可以关闭 导航栏&#xff08;实际是 公共事件&#xff0c;发送消息给systemUI来实控制&#x…

大模型微调终极方案:LoRA、QLoRA原理详解与LLaMA-Factory、Xtuner实战对比

文章目录 一、微调概述1.1 微调步骤1.2 微调场景 二、微调方法2.1 三种方法2.2 方法对比2.3 关键结论 三、微调技术3.1 微调依据3.2 LoRA3.2.1 原理3.2.2 示例 3.3 QLoRA3.4 适用场景 四、微调框架4.1 LLaMA-Factory4.2 Xtuner4.3 对比 一、微调概述 微调&#xff08;Fine-tun…

单片机-STM32部分:10-2、逻辑分析仪

飞书文档https://x509p6c8to.feishu.cn/wiki/VrdkwVzOnifH8xktu3Bcuc4Enie 安装包如下&#xff1a;根据自己的系统选择&#xff0c;目前这个工具只有window版本哦 安装方法比较简单&#xff0c;都按默认下一步即可&#xff0c;注意不要安装到中文路径哦。 其余部分参考飞书文档…

uniapp-商城-48-后台 分类数据添加修改弹窗bug

在第47章的操作中&#xff0c;涉及到分类的添加、删除和更新功能&#xff0c;但发现uni-popup组件存在bug。该组件的函数接口错误导致在小程序中出现以下问题&#xff1a;1. 点击修改肉类名称时&#xff0c;回调显示为空&#xff0c;并报错“setVal is not defined”&#xff0…

STM32-ADC模数转换器(7)

目录 一、ADC简介 二、逐次逼近型ADC 三、ADC基本结构图 四、规则组的四种转换模式 五、转换时间 对GPIO来说&#xff0c;它只能读取引脚的高低电平&#xff0c;使用了ADC模数转化器之后&#xff0c;就可以对高电平和低电平之间的任意电压进行量化&#xff0c;最终用一个变…

智能商品推荐系统技术路线图

智能商品推荐系统技术路线图 系统架构图 --------------------------------------------------------------------------------------------------------------- | 用户交互层 (Presentation Layer) …

【Docker系列】docker inspect查看容器部署位置

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

标量/向量/矩阵/张量/范数详解及其在机器学习中的应用

标量&#xff08;Scalar&#xff09;、向量&#xff08;Vector&#xff09;、矩阵&#xff08;Matrix&#xff09;、张量&#xff08;Tensor&#xff09;与范数&#xff08;Norm&#xff09;详解及其在机器学习中的应用 1. 标量&#xff08;Scalar&#xff09; 定义&#xff1…

【2025年】基于电脑的jdk1.8通过idea创建springboot2.x版本(非常简洁快速)

【2025年】基于电脑的jdk1.8通过idea创建springboot2.x版本 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是springboot的使用。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性。【帮帮志系列文章】&…

SierraNet协议分析使用指导[RDMA]| 如何设置 NVMe QP 端口以进行正确解码

在解码RoCEv2数据包&#xff08;包括TCP RDMA和RoCE RDMA&#xff09;时&#xff0c;若捕获的跟踪数据无法正确解码&#xff0c;通常需要执行特定的解码步骤。对于RoCE RDMA跟踪数据的处理&#xff0c;分析器主要采用两种方式获取必要信息以实现数据包解码&#xff1a; 首先&am…

JavaScript基础-局部作用域

在JavaScript中&#xff0c;理解不同种类的作用域是掌握这门语言的关键之一。作用域决定了变量和函数的可访问性&#xff08;即可见性和生命周期&#xff09;。与全局作用域相对应的是局部作用域&#xff0c;它限制了变量和函数只能在其定义的特定范围内被访问。本文将深入探讨…

李沐动手深度学习(pycharm中运行笔记)——09.softmax回归+图像分类数据集+从零实现+简洁实现

09.softmax回归图像分类数据集从零实现简洁实现&#xff08;与课程对应&#xff09; 目录 一、softmax回归 1、回归 vs 分类 2、经典分类数据集&#xff1a; 3、从回归到分类——均方损失 4、从回归到多类分类——无校验比例 5、从回归到多类分类——校验比例 6、softmax和…