当前位置: 首页 > news >正文

《程序员面试金典(第6版)》面试题 08.08. 有重复字符串的排列组合(回溯算法,全排列问题)C++

题目描述

有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。

示例1:

  • 输入:S = “qqe”
    输出:[“eqq”,“qeq”,“qqe”]

示例2:

  • 输入:S = “ab”
    输出:[“ab”, “ba”]

提示:

  • 字符都是英文字母。
    字符串长度在[1, 9]之间。

解题思路与代码

这道题一看还是一道关于排列的问题。只要有关排列的问题,我们都可以通过回溯法去解决。

方法一: 回溯法 + 使用unordered_set数据结构进行去重

如果没有做过《程序员面试金典(第6版)》面试题 08.07. 无重复字符串的排列组合(回溯算法,全排列问题)C++
这道题的小伙伴,先去做一下这道题。

这道题与上面链接的那道题非常像,只不过,这里字符串中的字符开始出现有重复的字符了。所以我们做这道题的时候就需要去重。我们直接用unordered_set这种数据结构去去一下重好了。代码与上道题的代码没什么区别,这里给出这道题的代码:

class Solution {
public:vector<string> permutation(string S) {unordered_set<string> result;backtracking(S,result,0);vector<string> vec;for(auto a : result){vec.push_back(a);}return vec;}void backtracking(string& S,unordered_set<string>& result,int begin){if(begin == S.size()){result.insert(S);return;}for(int i = begin;i < S.size(); ++i){swap(S[i],S[begin]);backtracking(S,result,begin+1);swap(S[i],S[begin]);}}
};

在这里插入图片描述

复杂度分析

时间复杂度:

  • 这段代码的时间复杂度主要取决于两个部分:backtracking 函数的执行次数以及将结果从 unordered_set 转移到 vector 的时间。

  • backtracking 函数的执行次数:对于长度为 n 的字符串,我们需要对每个字符进行排列组合,这会产生 n! 个排列。在回溯算法中,我们会遍历整个排列空间。因此,backtracking 函数的执行次数为 O(n!)。

  • 将结果从 unordered_set 转移到 vector:result 中最多有 n! 个元素。遍历 result 并将其中的元素插入 vector 的时间复杂度为 O(n!)。

  • 综合这两部分,总的时间复杂度为 O(n!)。

空间复杂度:

  • 空间复杂度主要取决于三个方面:递归调用栈的深度、结果存储在 unordered_set 中所占用的空间,以及结果向量 vec 所占用的空间。

  • 递归调用栈的深度:在回溯算法中,递归调用栈的深度等于字符串的长度 n。因此,递归调用栈的空间复杂度为 O(n)。

  • 结果存储在 unordered_set 中所占用的空间:result 中最多有 n! 个元素,每个元素是一个长度为 n 的字符串。因此,结果存储在 unordered_set 中所占用的空间复杂度为 O(n * n!)。

  • 结果向量 vec 所占用的空间:vec 中有 n! 个元素,每个元素是一个长度为 n 的字符串。因此,结果向量所占用的空间复杂度为 O(n * n!)。

  • 由于这三部分空间是算法使用的空间,因此总的空间复杂度为这三者之和,即 O(n) + O(n * n!) + O(n * n!) = O(2 * n * n!) = O(n * n!)。

  • 所以,这段代码的时间复杂度为 O(n!),空间复杂度为 O(n * n!)。

方法二 :对代码一的方法进行优化。

在这道题里,因为可能有重复的字符,方法一是直接用unordered_set在结果处进行去重,重复的答案不会被存进集合中,但这种方法不会减少递归的此时。那有没有一种方法,可以在做交换的时候就进行剪枝操作而进行去重呢,而去减少递归的次数呢?

答案当然是有,我们可以通过一个unordered_set去记住在当前的这一层循环里出现过哪些字符,如果出现了重复的字符,那我们就跳过这次交换,直接进入下一次交换。这样也同样达到了去重的目的,也减少了递归的次数。但是不好的一点是增加了内存的存储空间,因为每一层递归都要创建一个unordered_set去存储出现过的字符。

具体代码如下:

class Solution {
public:vector<string> permutation(string S) {vector<string> result;backtracking(S, result, 0);return result;}void backtracking(string &S, vector<string> &result, int begin) {if (begin == S.size()) {result.push_back(S);return;}unordered_set<char> used_chars;  // 用于存储已经在当前位置出现过的字符for (int i = begin; i < S.size(); ++i) {if (used_chars.find(S[i]) != used_chars.end()) {continue;  // 如果当前字符已经在当前位置出现过,则跳过这次交换}used_chars.insert(S[i]);  // 记录当前字符swap(S[i], S[begin]);backtracking(S, result, begin + 1);swap(S[i], S[begin]);}}
};

在这里插入图片描述

复杂度分析

通过这种剪枝策略,我们避免了搜索重复的路径,从而降低了时间复杂度。然而,在最坏情况下(如所有字符都不同),算法的时间复杂度仍然是 O(n!)。空间复杂度与之前的分析相同,为 O(n * n!)。虽然这种剪枝策略不能在理论上改进时间复杂度,但在有重复字符的情况下,实际运行效率会有所提升,但是同样每一层都会多创建出一个unordered_set去存储至多n个字符,会多消耗一部分的内存空间。

方法三,对代码二的再次优化!

这一次我们写了一个hasDuplicate函数来检查有没有重复出现的字符。这样就不会使用额外的内存空间去存储字符了。
因为begin的值肯定是要比i小的,因为i会递增,而begin不会,从而我们在这个函数中,去递增begin,看看有没有会出现与i相当的字符,如果出现了,就说明有重复,就要跳过这个循环!

class Solution {
public:vector<string> permutation(string S) {vector<string> result;backtracking(S,result,0);return result;}void backtracking(string& S,vector<string>& result,int begin){if(begin == S.size()) {result.push_back(S);return;}for(int i = begin; i < S.size(); ++i){if(hasDuplicate(S,begin,i)) continue;swap(S[i],S[begin]);backtracking(S,result,begin+1);swap(S[i],S[begin]);}}bool hasDuplicate(string& S, int begin,int end){for(int i = begin; i < end; ++i)if(S[i] == S[end]) return true;return false;}
};

在这里插入图片描述

复杂度分析

时间复杂度:

  • permutation 函数的时间复杂度主要取决于 backtracking 函数。在最坏情况下,回溯算法将尝试所有可能的排列组合,即 n!。
  • hasDuplicate 函数的时间复杂度为 O(n)(在 for 循环内部进行比较)。
  • backtracking 函数中调用了 hasDuplicate 函数,所以在最坏情况下,总时间复杂度为 O(n! * n)。

空间复杂度:

  • 结果向量 result 的空间复杂度为 O(n!),因为它需要存储所有排列组合。
  • 递归栈的空间复杂度为 O(n),因为最深的递归调用次数等于字符串的长度。
  • 总的空间复杂度为 O(n! + n)。

综上所述,该算法的时间复杂度为 O(n! * n),空间复杂度为 O(n! + n)。

虽然说,代码1,2,3的时间复杂度都是O(n!),但是在代码三的实际时间复杂度要比1,2快了不少。

总结

这道题不算一道特别难的题。但是呢,剪枝和去重,才是这道题的重中之重。写出简洁并且高效的回溯算法并不容易。我们还得去多学习多总结!

相关文章:

《程序员面试金典(第6版)》面试题 08.08. 有重复字符串的排列组合(回溯算法,全排列问题)C++

题目描述 有重复字符串的排列组合。编写一种方法&#xff0c;计算某字符串的所有排列组合。 示例1: 输入&#xff1a;S “qqe” 输出&#xff1a;[“eqq”,“qeq”,“qqe”] 示例2: 输入&#xff1a;S “ab” 输出&#xff1a;[“ab”, “ba”] 提示: 字符都是英文字母。…...

k8s API限流——server级别整体限流和客户端限流

1. 背景 为了防止突发流量影响apiserver可用性&#xff0c;k8s支持多种限流配置&#xff0c;包括&#xff1a; MaxInFlightLimit&#xff0c;server级别整体限流Client限流EventRateLimit, 限制eventAPF&#xff0c;更细力度的限制配置 1.1 MaxInFlightLimit限流 apiserver…...

在华为做了三年软件测试被裁了,我该怎么办

近年来&#xff0c;随着经济环境的变化和企业战略的调整&#xff0c;员工被裁员的情况变得越来越普遍。无论是因为企业经营困难还是因为业务调整&#xff0c;员工们都可能面临被裁员的风险。如果你也遇到了这样的情况&#xff0c;那么你应该怎么办呢&#xff1f; 首先&#xf…...

Spring cloud 限流的多种方式

在频繁的网络请求时&#xff0c;服务有时候也会受到很大的压力&#xff0c;尤其是那种网络攻击&#xff0c;非法的。这样的情形有时候需要作一些限制。本文主要介绍了两种限流方法&#xff0c;感兴趣的可以了解一下 目录 一、实战基于 Spring cloud Gateway 的限流 二、基于阿…...

Linux命令·top

top命令是Linux下常用的性能分析工具&#xff0c;能够实时显示系统中各个进程的资源占用状况&#xff0c;类似于Windows的任务管理器。下面详细介绍它的使用方法。top是一个动态显示过程,即可以通过用户按键来不断刷新当前状态.如果在前台执行该命令,它将独占前台,直到用户终止…...

springmvc之系列文章

springmvc之编程步骤 springmvc初始化过程 用WebServlet和WebFilter干掉web.xml 没有web.xml怎么写web程序 一次GET请求在springmvc中是的处理流程 springMVC的handler都有哪些类型 springmvc主要组件简单介绍 springmvc 的Servlet WebApplicationContext springmvc 的…...

Matlab实现深度学习(附上完整仿真源码)

文章目录简单案例完整仿真代码下载简单案例 深度学习是一种能够自动学习和提取数据特征的机器学习方法&#xff0c;它已经在图像识别、语音识别、自然语言处理等领域取得了显著的成果。而Matlab作为一个强大的数学计算工具&#xff0c;也提供了丰富的深度学习工具箱&#xff0…...

我的谷歌书签

Form 表单 | Element Plusa Vue 3 based component library for designers and developershttps://element-plus.gitee.io/zh-CN/component/form.html#%E5%AF%B9%E9%BD%90%E6%96%B9%E5%BC%8F three.js exampleshttp://www.yanhuangxueyuan.com/threejs/examples/#software_geo…...

day3 数据库技术考点汇总

一、重点知识点 基本概念&#xff1a;三级模式-两级映像、数据库设计数据库模型&#xff1a;E-R模型、关系模型、关系代数&#xff08;结合SQL语言&#xff09;规范化&#xff1a;函数依赖、健与约束、范式、模式分解事务并发&#xff1a;并发三种问题、三级封锁协议数据库新技…...

学剪辑难吗 如何使用会声会影2023做剪辑视频

很多剪辑初学者都问过一个问题&#xff0c;学剪辑难吗&#xff1f;其实不论学什么&#xff0c;只要用心学都不难&#xff0c;今天我们就来讲讲如何学做剪辑视频&#xff0c;感兴趣的小伙伴们不要走开&#xff01;一、学剪辑难吗 其实学剪辑并不是件难事&#xff0c;但是需要掌握…...

django学习日记

1、虚拟环境 virtualenv "加虚拟环境名字" 在当前目录下创建一个虚拟环境 进入虚拟环境执行activate进入该虚拟环境&#xff0c;再执行deactivate退出虚拟环境 安装一个包来管理虚拟环境&#xff0c;每次创建虚拟环境都放到同一位置&#xff0c;以及在任意位置都可…...

在线教学视频课程如何防止学员挂机?

阿酷TONY / 2023-3-31 / 长沙 / 原创 / 要不&#xff1f;交个朋友吧&#xff1f; 在线教学视频课程如何防止学员挂机&#xff1f;siri:这是个有意思的问题哈~~~在线教育、在线企业培训机构通常是如何处理的呢&#xff1f; 答&#xff1a;在视频播放过程中&#xff0c;弹出问题…...

【Redis】安装配置

文章目录Redis简介Redis版本迭代Redis安装配置官网地址操作系统环境基础查看本地redis版本修改配置文件docker容器安装redis测试linux版Redis简介 简介 与传统数据库关系(mysql)&#xff0c;Redis是key-value数据库(NoSQL一种)&#xff0c;mysql是关系数据库。Redis数据操作主要…...

ChatGPT批量生成文章-ChatGPT文章生成器

ChatGPT&#xff1a;一键批量生成高质量文章&#xff0c;提高生产效率&#xff01; 随着信息爆炸的时代&#xff0c;文本生产成为了各个行业必不可少的一部分。但面对高强度的生产需求&#xff0c;人力资源却难以跟上步伐。现在&#xff0c;我们有一款基于人工智能和自然语言处…...

Linux命令 ——sed

介绍 sed 是一种流式文本编辑器&#xff0c;常用于在 Unix 和类 Unix 系统中对文本进行处理。它可以将文本从标准输入或文件中读取&#xff0c;对其进行修改&#xff0c;然后将修改后的文本输出到标准输出或文件中。sed 是 “stream editor” 的缩写。 语法 sed 的基本语法为…...

C++常用字符串string方法

文章目录字符串string操作方法1. 类方法使用示例2. 头文件cstring方法使用示例字符串string操作方法 1. 类方法 在C中&#xff0c;引入string.h头文件可以使用C语言中的字符串操作函数。然而&#xff0c;C提供了一个更加方便的字符串类string&#xff0c;不需要引入string.h头…...

XML树结构和语法

文章目录一、XML 树结构二、XML 语法规则总结一、XML 树结构 XML 树结构 XML 文档形成了一种树结构&#xff0c;它从"根部"开始&#xff0c;然后扩展到"枝叶"。 一个 XML 文档实例 XML 文档使用简单的具有自我描述性的语法&#xff1a; <?xml vers…...

【Qt】Qt单元测试详解(四):Google Test 断言

1、创建测试工程 【Qt】Qt单元测试详解(一):通过QtCreator创建测试工程 2、添加测试代码 2.1 默认生成的代码 1)项目工程pro include(gtest_dependency.pri)TEMPLATE = app CONFIG += console c++14 CONFIG -= app_bundle CONFIG += thread CONFIG -= qtHEADERS += \t…...

句柄和指针的区别

句柄和指针都是一种数据结构&#xff0c;都常用于访问内存&#xff0c;下面介绍他们的一些不同点。 1 数据结构类型不同 指针的数据类型是无符号整数&#xff0c;占用4或8个字节&#xff08;在32位和64位系统中&#xff09;&#xff0c;它就像一个变量一样&#xff0c;这个变量…...

Linux 网络编程学习笔记——十四、多线程编程

目录 早期 Linux 不支持线程&#xff0c;直到 1996 年&#xff0c;Xavier Leroy 等人才开发出第一个基本符合 POSIX 标准的线程库 LinuxThreads 。但 LinuxThreads 效率低而且问题很多。自内核 2.6 开始&#xff0c;Linux 才真正提供内核级的线程支持&#xff0c;并有两个组织…...

JS 获取时区

JS 获取时区 啥是时区&#xff1f; 时区是地球上的区域使用同一个时间定义。以前&#xff0c;人们通过观察太阳的外置&#xff08;时角&#xff09;决定时间&#xff0c;这就使得不同经度的地方的时间各有不同&#xff0c;为了统一使用同一个时间&#xff0c;就引入了时区的概…...

【0183】PG内核客户端认证之将读取的token创建HbaToken(3 - 1)

文章目录 1. 将HbaToken添加到List指针变(续)1.1 示意图的一些问题1.2 修复后的示意图2. 将一行HBA所有字段添加到current_line2.1 代码实现2.2 示意图实现1. 将HbaToken添加到List指针变(续) 在【0182】PG内核客户端认证之将读取的token创建HbaToken(3) 一文中的第3节中…...

别把 OpenAI 太当回事,它远未达到替换前端的地步

最近几个月&#xff0c;我和很多初入行的开发人员交谈&#xff0c;他们对AI越来越感到焦虑。他们看到像GPT-4这样的工具展示的越来越令人印象深刻的演示&#xff0c;担心等他们掌握了HTML/CSS/JS&#xff0c;就没有任何工作机会了。这种情绪现在在Twitter上广泛存在&#xff1a…...

前端基础HTML、CSS--8(CSS-5)

目标&#xff1a; 能够说出为什么要用定位 能够说出定位的4种分类 能够说出4种定位各自的特点 能够说出为什么常用子绝父相布局 能够写出淘宝轮播图布局 能够说出显示隐藏的2种方式以及区别 目录&#xff1a; 定位 综合案例 网页布局总结 元素的显示与隐藏 1.定位 …...

基于ASP网络办公OA系统的设计与实现

本系统是一个企业办公自动化系统&#xff0c;它简单易操作&#xff0c;第一次使用的用户只需申请一个本人的帐户&#xff0c;就可以方便的使用本系统了。 系统的用户分为3类&#xff0c;第一类为普通用户,第二类为管理员&#xff0c;第三类为超级用户。 1通过Web添加文件。操…...

C语言计算机二级/C语言期末考试 刷题(五)

收集了一些经典C语言计算机二级和C语言期末考试题库 整理不易&#xff0c;大家点赞收藏支持一下 祝大家计算机二级和期末考试都高分过 系列文章&#xff1a; C语言计算机二级/C语言期末考试 刷题&#xff08;一&#xff09; C语言计算机二级/C语言期末考试 刷题&#xff08;二…...

2023-04-03 grafana-源码编译启动及添加prometheus数据源

摘要: 记录grafana-源码编译启动及添加prometheus数据源 代码仓库: https://github.com/adofsauron/tiflow-dev/tree/main/trunk/grafana-9.4.7 依赖: 必须为go1.19版本&#xff0c;低于此版本则编译报错 go version go1.19.2 linux/amd64 yum install -y yarnpkg 编译: ht…...

微软New Bing(GPT-4)写的Delphi诗歌

你好&#xff0c;这是Bing。我很高兴你对Delphi感兴趣。Delphi是一种编程语言&#xff0c;可以用来开发各种应用程序。我尝试写了一首赞美Delphi的诗歌&#xff0c;希望你喜欢&#xff1a; Delphi&#xff0c;Delphi&#xff0c;你是我的灵感 你让我创造出美妙的程序 你的语法…...

【进程地址空间】

目录&#xff1a;前言一、进程地址空间&#xff08;一&#xff09;进程地址空间是什么&#xff08;二&#xff09;为什么要有进程地址空间&#xff08;三&#xff09;重新理解进程地址空间前言 打怪升级&#xff1a;第52天一、进程地址空间 打怪升级之路开始&#xff01;&…...

递归dfs入门

做题方法&#xff1a;确定枚举顺序&#xff0c;画出递归树 递归实现指数型枚举 题目编号&#xff1a; acwing.92.递归实现指数型枚举 题目描述&#xff1a; 从 1∼n 这 n 个整数中随机选取任意多个&#xff0c;输出所有可能的选择方案。 输入格式&#xff1a; 输入一个整数 n…...