编程题-字母异位词分组(中等-重点)

题目:

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

解法一(for循环遍历-时间复杂度超限):

由于互为字母异位词的两个字符串包含的字母相同,因此对两个字符串分别进行排序之后得到的字符串一定是相同的,故可以将排序后的字符串作为哈希表的键。

依次遍历strs中的每个元素,通过建立unordered_map<string, int>哈希表对新添加的strs的元素和strs中的其他元素执行for循环进行判断比较,在for循环中对与指定strs的元素相同的字母异位词进行添加,与指定strs元素不同的字母异位词跳过,并删除for循环遍历后的与指定strs元素相同的字母异位词的组合,进入下一个for循环判断另一个strs指定元素的字母异位词。其中默认strs中的第一个元素为指定的本方法时间复杂度超限仅与下面的方法作为参考,并不是本题的正确解,如下为笔者代码:

class Solution {
public:
    void kyk(vector<string> strs, vector<vector<string>>& results, vector<string> result){
        int length = strs.size();
        if(length==0){
            return;
        }
        stack<int> intStack1;
        intStack1.push(0);
        result.push_back(strs[0]);
        for(int i=1; i<length; i++){
            unordered_map<string,int> hashTable;
            string str1 = strs[0];
            string str2 = strs[i];
            sort(str1.begin(),str1.end());
            sort(str2.begin(),str2.end());
            hashTable[str1]++;
            auto it = hashTable.find(str2);
            if(it!=hashTable.end()){
                intStack1.push(i);
                result.push_back(strs[i]);
            }
        }
        while(!intStack1.empty()){
            int index = intStack1.top();
            strs.erase(strs.begin() + index);
            intStack1.pop();
        }
        results.push_back(result);
        result = {};
        kyk(strs, results, result);
    }
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        vector<vector<string>> results;
        vector<string> result;
        int length = strs.size();
        kyk(strs, results, result);
        return results;
    }
};

解法二(排序+高阶哈希表使用):

上述方法使用sort排序方法和unordered_map<string, int>哈希表对字母异位词进行分组判断,但是在进行for循环遍历时会导致数组元素的重复访问,导致时间复杂度升到,因此为了防止数组元素的重复访问,我们采用unordered_map<string, vector<string>>高阶的哈希表数据结果来降低时间复杂度,其中哈希表存储每一组字母异位词,哈希表的键为一组字母异位词的标志,哈希表的值为一组字母异位词列表。

遍历每个字符串,对于每个字符串,得到该字符串所在的一组字母异位词的标志,将当前字符串加入改组字母异位次的列表中。遍历全部字符串后,哈希表中的每个键值对即为一组字母异位词。如下为实现代码:

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        创建高阶哈希表数据结构,其中键为一组字母异位词的标志,值为一组字母异位词的列表。
        unordered_map<string, vector<string>> mp;
        //一次遍历实现所有字母异位词添加到mp高阶哈希表中
        for (string& str: strs) {
            string key = str;
            sort(key.begin(), key.end());
            mp[key].emplace_back(str);
        }
        //for循环访问高阶哈希表中所有元素,并将其添加到ans最终返回结果中
        vector<vector<string>> ans;
        for (auto it = mp.begin(); it != mp.end(); ++it) {
            ans.emplace_back(it->second);
        }
        return ans;
    }
};

上述方法仅执行了单层的for循环,并没有嵌套多层for循环。因此时间复杂度:O(nklogk),其中 n 是 strs 中的字符串的数量,k 是 strs 中的字符串的的最大长度。需要遍历 n 个字符串,对于每个字符串,需要 O(klogk) 的时间进行排序以及 O(1) 的时间更新哈希表,因此总时间复杂度是 O(nklogk)。空间复杂度:O(nk),其中 n 是 strs 中的字符串的数量,k 是 strs 中的字符串的的最大长度。需要用哈希表存储全部字符串。

解法三(计数+高阶哈希表):

由于互为字母异位词的两个字符串包含的字母相同,因此除了上述采用排序的方式确定两个字母异位词是否是相同的方式之外,也可以通过计数两个字符串中相同字母出现的次数来进行判断,将每个字母出现的次数使用字符串表示,作为哈希表的键。

由于字符串只包含小写字母,因此对于每个字符串,可以使用长度为26的数组记录每个字母出现的次数。需要注意的是,在使用数组作为哈希表的键时,不同语言的支持程度不同,因此不同语言的实现方式也不同。如下为实现代码:

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        // 自定义对 array<int, 26> 类型的哈希函数
        auto arrayHash = [fn = hash<int>{}] (const array<int, 26>& arr) -> size_t {
            return accumulate(arr.begin(), arr.end(), 0u, [&](size_t acc, int num) {
                return (acc << 1) ^ fn(num);
            });
        };

        unordered_map<array<int, 26>, vector<string>, decltype(arrayHash)> mp(0, arrayHash);
        for (string& str: strs) {
            array<int, 26> counts{};
            int length = str.length();
            for (int i = 0; i < length; ++i) {
                counts[str[i] - 'a'] ++;
            }
            mp[counts].emplace_back(str);
        }
        vector<vector<string>> ans;
        for (auto it = mp.begin(); it != mp.end(); ++it) {
            ans.emplace_back(it->second);
        }
        return ans;
    }
};

时间复杂度:O(n(k+∣Σ∣)),其中 n 是 strs 中的字符串的数量,k 是 strs 中的字符串的的最大长度,Σ 是字符集,在本题中字符集为所有小写字母,∣Σ∣=26。需要遍历 n 个字符串,对于每个字符串,需要 O(k) 的时间计算每个字母出现的次数,O(∣Σ∣) 的时间生成哈希表的键,以及 O(1) 的时间更新哈希表,因此总时间复杂度是 O(n(k+∣Σ∣))。空间复杂度:O(n(k+∣Σ∣)),其中 n 是 strs 中的字符串的数量,k 是 strs 中的字符串的最大长度,Σ 是字符集,在本题中字符集为所有小写字母,∣Σ∣=26。需要用哈希表存储全部字符串,而记录每个字符串中每个字母出现次数的数组需要的空间为 O(∣Σ∣),在渐进意义下小于 O(n(k+∣Σ∣)),可以忽略不计。

笔者小记:

1、采用sort()函数不仅可以对数组进行排序,而且可以对string类型的字符串进行排序,确定两个字符串是否是字母异位词,可以加快两个string类型字符串的判断。

2、采用高阶哈希表unordered_map<string, vector<string>>可以有效地降低时间复杂度,仅通过一次for循环便可以遍历并添加所有满足条件的键值对,减少利用多层for循环导致元素重复访问引起的时间超限。(哈希表中键为同一组字母异位词的标志,值为同一组字母异位词的列表)

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

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

相关文章

Spring Boot整合DeepSeek实现AI对话(API调用和本地部署)

本篇文章会分基于DeepSeek开放平台上的API&#xff0c;以及本地私有化部署DeepSeek R1模型两种方式来整合使用。 本地化私有部署可以参考这篇博文 全面认识了解DeepSeek利用ollama在本地部署、使用和体验deepseek-r1大模型 Spring版本选择 根据Spring官网的描述 Spring AI是一…

阿里云IOT消息处理

文章主要讲述了阿里云IOT平台如何处理设备上报的消息、如何将消息路由到不同的处理逻辑、如何进行消息转发与转换等操作。 一、接收IOT消息 1.创建订阅 2.案列代码 官网案例代码&#xff1a;如何将AMQP JMS客户端接入物联网平台接收消息_物联网平台(IoT)-阿里云帮助中心 代码…

YOLO11网络结构以及改进1

YOLO11 1.YOLO11网络结构图在哪里&#xff1f;2.对应的网络结构图3.每一个模块详解3.1 Conv模块3.2关于卷积模块3.3 关于给各个模块指定参数的细节 4.加入CBAM 1.YOLO11网络结构图在哪里&#xff1f; 2.对应的网络结构图 3.每一个模块详解 3.1 Conv模块 位置&#xff1a;ultr…

数据结构——队列、哈希存储(2025.2.11)

目录 一、队列 1.定义 2.应用 3.分类 &#xff08;1&#xff09;逻辑结构 &#xff08;2&#xff09;物理结构 顺序队列 链式队列 二、哈希存储 1.定义 2.哈希冲突 &#xff08;1&#xff09;开放定址法 &#xff08;2&#xff09;再哈希法 &#xff08;3&#xf…

鸿蒙Next开发-添加水印以及点击穿透设置

在鸿蒙Next中&#xff0c;为App全局添加水印可以通过以下方式实现&#xff0c;其中通过窗口添加水印是一种常见且高效的方式。以下是具体方案和实现细节&#xff1a; 一、全局水印的实现方式 1. 窗口叠加水印&#xff08;首选、推荐&#xff09; 原理&#xff1a;在应用的主窗口…

C++算法竞赛基础语法-9

快速排序是一种高效的排序算法&#xff0c;由C. A. R. Hoare在1960年提出&#xff0c;基本思想是分治法&#xff08;Divide and Conquer&#xff09;策略&#xff0c;通过递归将一个大问题分解为若干个较小的子问题&#xff0c;然后合并这些子问题的解来解决原始问题 快速排序…

DeepSeek v3 技术报告阅读笔记

注 本文参考 DeepSeek-v3 / v2 / v1 Technical Report 及相关参考模型论文本文不包括基础的知识点讲解&#xff0c;为笔记/大纲性质而非教程&#xff0c;建议阅读技术报告原文交流可发送至邮箱 henryhua0721foxmail.com 架构核心 核心&#xff1a; MLA 高效推理DeepSeekMOE 更…

jemalloc 5.3.0的base模块的源码及调用链使用场景的详细分析

一、背景 这篇博客&#xff0c;我们继续之前的 由jemalloc 5.3.0初始化时的内存分配的分析引入jemalloc的三个关键概念及可借鉴的高性能编码技巧-CSDN博客 博客里对初始化分配逻辑进行分析&#xff0c;已经涉及到了jemalloc 5.3.0里的非常重要的base模块的一部分逻辑&#xff…

从零搭建微服务项目(第5章——SpringBoot项目LogBack日志配置+Feign使用)

前言&#xff1a; 本章主要在原有项目上添加了日志配置&#xff0c;对SpringBoot默认的logback的配置进行了自定义修改&#xff0c;并详细阐述了xml文件配置要点&#xff08;只对日志配置感兴趣的小伙伴可选择直接跳到第三节&#xff09;&#xff0c;并使用Feign代替原有RestT…

2024最新版JavaScript逆向爬虫教程-------基础篇之Chrome开发者工具学习

目录 一、打开Chrome DevTools的三种方式二、Elements元素面板三、Console控制台面板四、Sources面板五、Network面板六、Application面板七、逆向调试技巧 7.1 善用搜索7.2 查看请求调用堆栈7.3 XHR 请求断点7.4 Console 插桩7.5 堆内存函数调用7.6 复制Console面板输出 工…

Elasticsearch+Logstash+Kibana可视化集群部署

文章目录 1.组件介绍简述2.集群规划3.Es组件部署4.Logstash组件部署5.Kibana组件部署6.Kibana的基础使用 1.组件介绍简述 Elasticsearch&#xff1a;开源实时分布式搜索和分析引擎&#xff0c;支持大规模数据存储和高吞吐量&#xff0c;提供丰富的搜索功能和可扩展性。 Logsta…

08模拟法 + 技巧 + 数学 + 缓存(D3_数学)

目录 1. 多数元素 1.1. 题目描述 1.2. 解题思路 方法一&#xff1a;哈希表 方法二&#xff1a;排序 方法三&#xff1a;随机化 方法四&#xff1a;分治 方法五&#xff1a;Boyer-Moore 投票算法 2. 按规则计算统计结果 2.1. 题目描述 2.2. 解题思路 3. 整数拆分 3.…

基于IOS实现各种倒计时功能

ZJJTimeCountDown 效果图 特点&#xff1a; 1、已封装&#xff0c;支持自定义 2、支持文本各种对齐模式 3、各种效果都可以通过设置 ZJJTimeCountDownLabel 类属性来实现 4、支持背景图片设置 5、分文本显示时间时&#xff0c;支持设置文字大小&#xff0c;来动态设置每个文本…

【TS合成MP4】你怎么专打裂开的切片呀

写在前面&#xff1a;本博客仅作记录学习之用&#xff0c;部分图片来自网络&#xff0c;如需引用请注明出处&#xff0c;同时如有侵犯您的权益&#xff0c;请联系删除&#xff01; 文章目录 前言TS与MP4格式概述TS与MP4格式概述TS合成MP4的需求背景TS合成MP4的方法概述 合并方法…

【动手学强化学习】01初探强化学习

文章目录 什么是强化学习强化学习解决的问题强化学习的独特性 什么是强化学习 强化学习是机器通过与环境交互来实现目标的计算方法。智能体与环境的交互方式如图所示&#xff0c;在每一轮交互中&#xff0c;智能体根据感知状态经过自身计算给出本轮动作&#xff0c;将其作用于…

C++,STL容器适配器,priority_queue:优先队列深入解析

文章目录 一、容器概览与核心特性核心特性速览二、底层实现原理1. 二叉堆结构2. 容器适配器架构三、核心操作详解1. 容器初始化2. 元素操作接口3. 自定义优先队列四、实战应用场景1. 任务调度系统2. 合并K个有序链表五、性能优化策略1. 底层容器选择2. 批量建堆优化六、注意事项…

duckdb导出Excel和导出CSV速度测试

运行duckdb数据库 D:>duckdb v1.2.0 5f5512b827 Enter “.help” for usage hints. Connected to a transient in-memory database. Use “.open FILENAME” to reopen on a persistent database. 生成模拟数据&#xff0c;10个列&#xff0c;100万行数据&#xff1b; --…

k8s集群离线安装kuberay operator

1,安装方式 采用helm安装方式&#xff0c;首先下载对应的helm chart&#xff0c;这里采用v1.2.2版本&#xff0c;下载地址&#xff1a; https://github.com/ray-project/kuberay-helm/releases/tag/kuberay-operator-1.2.2 2,解压并修改镜像源 由于是在内网环境下搭建&#…

结构形模式---适配器模式

适配器模式是一种结构形模式&#xff0c;主要用于不同在两个互不兼容的类或者库之间增加一个转换。 适配器模式的实现由两种方式&#xff0c;一种是适配器对象&#xff0c;一种是适配器类。 适配器是对象是将第三方接口通过对象调用引入到适配器中。 适配器类是通过多继承将…

面向SDV的在环测试深度解析——概述篇

1.引言 在汽车行业迈向软件定义汽车&#xff08;SDV&#xff09;的进程中&#xff0c;传统的硬件在环&#xff08;HIL&#xff09;测试方案在面对新的技术架构和需求时逐渐显露出局限性。一方面&#xff0c;现代汽车的电子电气架构日益复杂&#xff0c;高性能计算&#xff08;…