递归(三)—— 初识暴力递归之“字符串的全部子序列”

题目1 : 打印一个字符串的全部子序列

题目分析:

解法1:非递归方法

我们通过一个实例来理解题意,假设字符串str = “abc”,那么它的子序列都有那些呢?" ", “a”, “b”,“ab”,"c","ac","bc","abc", 一共8个,其中有一个是空串。怎么得到这个结果的?我的眼就是尺,看一眼就知道了。但如果这个字符串的长度是10呢,30呢?看一眼怎么够!除了多看几眼,是不是也要找个纸、笔写写画画呢?

第一笔: a 的存在情况有两种:存在 或 不存在;

第二笔: b 的存在情况有两种:存在 或 不存在;

第三笔: c 的存在情况有两种:存在 或 不存在;

这三笔当然不能各自为营的画在纸上,第二笔和第三笔是在前一笔的基础上画出的。

第二笔:画出b的情况

第三笔:画出c的情况

现在,通过上图我们得到了子字符串的所有结果,"abc","ab","ac","a","bc","b","c","" 如果字符串str = "abcd",我们也有思路落下第四笔,即在'c' 的下方,给每一个字符串补充上d的存在与不存在这两个情况,于此同时,我们也总结出了计算子字符串的规律:

1.  从原字符串str 的index== 0 开始遍历字符串,直到index == 原字符串长度 结束。 (即:‘a’\rightarrow‘c’)

2. 当index == i 时, str[i] 的两种情况(存在或不存在)是添加到index == i-1 所得出的子串基础上的,这一条规律也是迭代的关键。

代码实现

//代码段1
#include <string>
#include <List>using std::string;
using std::list;class SubStr {
public:void process_1(string strPre, list<string>& ans);};void SubStr::process_1(string strPre,list<string>& ans) {if (strPre.length() < 1) return;string str = "";str = str + strPre[0];ans.push_back(str);ans.push_back("");for (size_t j = 1; j < strPre.length(); j++) {int count = ans.size();for (list<string>::iterator item = ans.begin();  item !=  ans.end(); item++) {ans.push_back(*item + strPre[j]);count--;if (!count)break;}}return;
}#define SUBSTRint main() {
#ifdef SUBSTRSubStr subStrObj;string str = "abc";list<string> ans;subStrObj.process_1(str, ans);for (list<string>::iterator item = ans.begin(); item != ans.end(); item++) {std::cout << *item << std::endl;}
#endifreturn 0;
}

运行结果

解法2:递归方法

在解法1中我们已经明白了子序列的含义,也明白了对于字符串str上任意位置上的字符str[i] 在子序列中都有两种可能性,即存在与不存在。假设 str = “abc”, 初始状态就是str[0~2] 字符都不存在的状态,即空串。

我们用枚举的方式,找到所有可能性,见表1.

我们给出的初始状态是各位都不存在,那么对于str[2] 来说,str[0~1] 不存在是确定的,是不可更改的事实,但是str[2]可选择,可以是不存在,例如子序列1;也可以是存在,例如子序列2.

到此,在str[0~1]不存在的条件下,所有的可能性都找到了:ans = {“”, “c”}

继续枚举,str[0]不存在,str[1]存在的条件下,str[2] 的可能性?见子序列3 和 子序列4.

ans = {"", "c", "b", "bc"};

继续枚举,str[0]存在,其后str[1~2] 的各种可能性:子序列5 ~ 子序列8;

ans={"", "c", "b", "bc", "a", "ac","ab","abc"}

表二中给出了当字符串的长度增加的枚举情况,str= "abcd";

表1
子序列abc子序列值
1\times\times\times" "
2\times\times\veec
3\times\vee\timesb
4\times\vee\veebc
5\vee\times\timesa
6\vee\times\veeac
7\vee\vee\timesab
8\vee\vee\veeabc
表2
子序列abcd子序列值
1\times\times\times\times" "
2\times\times\times\veed
3\times\times\vee\timesc
4\times\times\vee\veecd
5\times\vee\times\timesb
6\times\vee\times\veebc
7\times\vee\vee\timesbc
8\times\vee\vee\veebcd
9\vee\times\times\timesa
10\vee\times\times\veead
11\vee\times\vee\timesac
12\vee\times\vee\veeacd
13\vee\vee\times\timesab
14\vee\vee\times\veeabc
15\vee\vee\vee\timesabc
16\vee\vee\vee\veeabcd

通过两个表的枚举情况,我们可以得出这样的结论:

1. 每一个子串都隐含了各个字符的存在情况,比如表2中的子序列3 == ”c“, 其实用真值表值来表示的话,它就是0010, 第3位为1,表示c存在,其他位为0,表示不存在,也就是说我们遍历,产生子序列的时候,是对原字符串的每个字符都会遍历到的,只是选择它在子序列中存在或不存在。

2. 当遍历到第i位时, 我们认为 0 ~ i-1 位的情况是已经确定的,第i位以及其后面的各位的情况是有待枚举的。例如表2中子序列13~ 16, str[0~1] 已经确定,即存在,枚举出str[2~3] 的所有情况。

代码实现

//代码段2
#include <string>
#include <List>using std::string;
using std::list;class SubStr {
public:void process_2(string strPre, int index, list<string>& ans, string path);
};
/*
strPre: 原字符串
index:表示字符串的下标,即位置,当然来到了strPre[index];
ans:存放子序列
path:strPre[0~index-1] 的字符情况
*/
void SubStr::process_2(string strPre, int index, list<string>& ans, string path) {if (index == strPre.length()){  //已经遍历完了整个路径,即strPre[0 ~ strPer.length()-1]都遍历了,确定了各位的存在与否, 将这个路径存入结构体ans.push_back(path);return;}process_2(strPre, index + 1, ans, path); // 此子序列中strPre[index]不存在,即没有要strPre[index]字符process_2(strPre, index + 1, ans, path + strPre[index]);// 此子序列中strPre[index]存在,即要了strPre[index]字符	
}#define SUBSTRint main() {#ifdef SUBSTRstring str = "abc";list<string> ans;string path = "";SubStr subStrObj;subStrObj.process_2(str, 0, ans, path);std::cout << "子序列的数量是:" << ans.size() << std::endl;int count = 0;for (list<string>::iterator item = ans.begin(); item != ans.end(); item++) {std::cout <<"sub string " << ++count << ": "<<  * item << std::endl;}#endifreturn 0;
}

运行结果:

表1:

表2

小技巧:

代码中使用了STL容器来存放结果ans, 但是为什么使用list 而不是vector呢?在编写代码时具体使用什么数据结构,需要根据实际的数据类型、数据规模以及其对数据所要执行的操作特点来决定。

vector具有连续的内存空间,支持随机访问,如果需要高效的随机访问,而很少使用插入和删除的操作,使用vector

list 具有一段不连续的内存空间,如果需要高频次的插入和删除操作,较少执行随机存取,使用list。

本题目的场景更适合使用list。

补充问题:打印一个字符串的全部子序列,要求不出现重复子序列。

问题分析:

这个问题可以分两个思路来解决:

1. 使用代码段2中的函数process_2 找到所有的子序列,在打印时做去重处理;

2. 定义函数precess_3, 函数实现思路与process_2一样,只是用于存放子序列的数据结构由list改为set, 因为set 不存放相同的数据元素。

我们完成process_3 的代码实现。

代码实现:

//代码段3
#include <string>
#include <List>
#include <set>
using std::string;
using std::list;
using std::set;class SubStr {
public:void process_3(string strPre, int index, set<string>& ans, string path);
};
/*
strPre: 原字符串
index:表示字符串的下标,即位置,当然来到了strPre[index];
ans:存放子序列
path:strPre[0~index-1] 的字符情况
*/
void SubStr::process_3(string strPre, int index, list<string>& ans, string path) {if (index == strPre.length()){  //已经遍历完了整个路径,即strPre[0 ~ strPer.length()-1]都遍历了,确定了各位的存在与否, 将这个路径存入结构体ans.insert(path);return;}process_3(strPre, index + 1, ans, path); // 此子序列中strPre[index]不存在,即没有要strPre[index]字符process_3(strPre, index + 1, ans, path + strPre[index]);// 此子序列中strPre[index]存在,即要了strPre[index]字符	
}#define SUBSTRint main() {#ifdef SUBSTRstring str = "abcc";set<string> ans;string path = "";SubStr subStrObj;subStrObj.process_3(str, 0, ans, path);for (list<string>::iterator item = ans.begin(); item != ans.end(); item++) {std::cout << *item << std::endl;}#endifreturn 0;
}

运行结果:

如果用代码段2(没有去重功能)打印str="abcc" 的子序列:

 

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

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

相关文章

Vue的民族民俗文化分享平台-计算机毕业设计源码22552

基于Vue的民族民俗文化分享平台设计与实现 摘 要 本文介绍了一种基于Vue.js前端框架和Express后端框架的民族民俗文化分享平台的设计和实现。该平台旨在通过线上方式&#xff0c;促进民族民俗文化的传播与分享&#xff0c;增强公众对多元文化的了解和认同。 平台为普通用户提供…

前后端的导入、导出、模板下载等写法

导入&#xff0c;导出、模板下载等的前后端写法 文章目录 导入&#xff0c;导出、模板下载等的前后端写法一、导入实现1.1 后端的导入1.2 前端的导入 二、基础的模板下载2.1 后端的模板下载-若依基础版本2.2 前端的模板下载2.3 后端的模板下载 - 基于资源文件读取2.4 excel制作…

24西安电子科技大学马克思主义学院—考研录取情况

01、马克思主义学院各个方向 02、24马克思主义学院近三年复试分数线对比 PS&#xff1a;马院24年院线相对于23年院线增加15分&#xff0c;反映了大家对于马克思主义理论学习与研究的热情高涨&#xff0c;也彰显了学院在人才培养、学科建设及学术研究等方面的不断进步与成就。 6…

直接更新flowable数据库的流程定义信息的一种方法

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; h…

文章解读与仿真程序复现思路——太阳能学报EI\CSCD\北大核心《绿电交易场景下计及温控负荷的高铁站两阶段调度策略》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

【第21章】MyBatis-Plus多数据源支持

文章目录 前言一、dynamic-datasource1. 特性2. 约定3. 使用方法3.1 引入依赖3.2 配置数据源3.3 使用 DS 切换数据源 二、mybatis-mate1.特性2.使用方法2.1 配置数据源2.2 使用 Sharding 切换数据源2.3 切换指定数据库节点 三、实战1. 引入库2. 配置3. 使用 DS 切换数据源4. 测…

vue项目打包部署后 浏览器自动清除缓存问题(解决方法)

vue打包部署后 浏览器缓存问题&#xff0c;导致控制台报错ChunkLoadError: Loading chunk failed的解决方案 一、报错如下&#xff1a; 每次build打包部署到服务器上时&#xff0c;偶尔会出现前端资源文件不能及时更新到最新&#xff0c;浏览器存在缓存问题&#xff0c;这时在…

Pandas数据可视化详解:大案例解析(第27天)

系列文章目录 Pandas数据可视化解决不显示中文和负号问题matplotlib数据可视化seaborn数据可视化pyecharts数据可视化优衣库数据分析案例 文章目录 系列文章目录前言1. Pandas数据可视化1.1 案例解析&#xff1a;代码实现 2. 解决不显示中文和负号问题3. matplotlib数据可视化…

ListBox自动滚动并限制显示条数

1、实现功能 限制ListBox显示的最大条数&#xff1b; ListBox自动滚动&#xff0c;显示最新行&#xff1b; 2、C#代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using Syst…

JUC并发编程基础(包含线程概念,状态等具体实现)

一.JUC并发编程基础 1. 并行与并发 1.1 并发: 是在同一实体上的多个事件是在一台处理器上"同时处理多个任务"同一时刻,其实是只有一个事件在发生. 即多个线程抢占同一个资源. 1.2 并行 是在不同实体上的多个事件是在多台处理器上同时处理多个任务同一时刻,大家…

【C++】main函数及返回值深度解析

一.main函数介绍 1.main函数怎么写 #include <iostream>int main() {// 程序的代码放在这里std::cout << "Hello, World!" << std::endl;return 0; }在这个例子中&#xff1a; #include <iostream> 是预处理指令&#xff0c;它告诉编译器…

在昇腾服务器上使用llama-factory对baichuan2-13b模型进行lora微调

什么是lora微调 LoRA 提出在预训练模型的参数矩阵上添加低秩分解矩阵来近似每层的参数更新&#xff0c;从而减少适配下游任务所需要训练的参数。 环境准备 这次使用到的微调框架是llama-factory。这个框架集成了对多种模型进行各种训练的代码&#xff0c;少量修改就可使用。 …

小红书矩阵系统源码:赋能内容创作与电商营销的创新工具

在内容驱动的电商时代&#xff0c;小红书凭借其独特的社区氛围和用户基础&#xff0c;成为品牌营销和个人创作者不可忽视的平台。小红书矩阵系统源码&#xff0c;作为支撑这一平台的核心技术&#xff0c;提供了一系列的功能和优势&#xff0c;助力用户在小红书生态中实现更高效…

Windows 安装hadoop 3.4

目录 安装 下载 设置环境变量 配置 修改&#xff1a;hadoop-env.cmd 修改&#xff1a;core-sit.xml 修改&#xff1a;hdfs-site.xml 修改&#xff1a;mapred-site.xml 修改&#xff1a;yarn-site.xml 运行 格式化HDFS文件系统 启动&#xff1a;hadoop 启动&#xf…

python-21-零基础自学python 写了一个彩票 发现买彩票中了真的是天选

学习内容&#xff1a;《python编程&#xff1a;从入门到实践》第二版 知识点&#xff1a; from random import choice、choice&#xff08;&#xff09;函数用法、while循环 练习内容&#xff1a; 练习9-14&#xff1a;彩票 创建一个列表或元组&#xff0c;其中包含10个数…

JAVA基础知识(上)

# 一、说说&和&&的区别? 作为运算符&#xff1a;& 将二进制的每一位进行与运算 作为逻辑运算符&#xff1a;两者都是与&#xff0c;&& 如果左边为假则终止右边运算&#xff0c;即短路运算。& 则需要把两边的比较执行完 # 二、int和Integer的区…

Steam平台的辉煌轨迹:数字游戏革命的领航者

在数字世界的浩瀚星空中&#xff0c;有一颗恒星以其耀眼的光芒照亮了无数游戏爱好者的道路&#xff0c;它就是Valve公司的杰作——Steam平台。自2003年横空出世以来&#xff0c;Steam不仅颠覆了传统游戏分发的模式&#xff0c;更以其卓越的创新能力和前瞻性的战略眼光&#xff…

CSS - 深入理解选择器的使用方式

CSS基本选择器 通配选择器元素选择器类选择器id 选择器 通配选择器 作用&#xff1a;可以选中所有HTML元素。语法&#xff1a; * {属性名&#xff1b;属性值; }举例&#xff1a; /* 选中所有元素 */ * {color: orange;font-size: 40px; }在清除样式方面有很大作用 元素选择器…

JAVA基础知识(下)

一、String相关面试题 1. 为什么 String 在 java 中是不可变的? - 如果不是不可变的&#xff1a;这种情况根本不可能&#xff0c;因为在字符串池的情况下&#xff0c;一个字符串对象/文字&#xff0c;例如 “Test” 已被许多参考变量引用&#xff0c; 因此如果其中任何一个更…

ubuntu下载Nginx

一、Nginx下载安装&#xff08;Ubuntu系统&#xff09; 1.nginx下载 sudo apt-get install nginx2.nginx启动 启动命令 sudo nginx重新编译(每次更改完nginx配置文件后运行&#xff09;&#xff1a; sudo nginx -s reload3.测试nginx是否启动成功 打开浏览器访问本机80端口…