每日刷题|回溯法解决全排列问题第二弹之解决字符串、字母大小排列问题

++食用指南:本文为作者刷题中认为有必要记录的题目++

前置知识 回溯法经典问题之全排列

♈️++今日夜电波:带我去找夜生活---告五人++

0:49 ━━━━━━️💟──────── 4:59

🔄 ◀️ ⏸ ▶️ ☰

💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


目录

回溯法的理解

💮一、字符串的排列

🌺二、字母大小写全排列


回溯法的理解

本文参考了一位大佬的题解,详细的介绍了回溯法:链接******

上一篇刷题文: 回溯法经典问题之子集

记住一句话:**for循环横向遍历,递归纵向遍历,回溯不断调整结果集。**这句话将从始至终贯穿我们对于以上问题的回溯解决办法。


💮一、字符串的排列

题目链接:剑指 Offer 38. 字符串的排列

题目描述:

输入一个字符串,打印出该字符串中字符的所有排列。

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例:

复制代码
输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]

限制:

1 <= s 的长度 <= 8

本题思路:

***首先:***采用经典的"回溯三部曲":

1、定义两个全局变量,一个用来存放符合条件单一结果(path),一个用来存放符合条件结果的集合(result)。

2、回溯的主体,回溯终止条件。path保存一组数据,每次遍历到叶子节点,再插入到result中,并且回溯到上一个节点。

3、单层搜索的过程。for循环用来横向遍历,递归的过程是纵向遍历。

根据题意我们做出一定的改动:

由于本题是以string容器的形式传递的字符串,对此,我们的path也应当转变为相应的形式。题目以及例子很明确的表现了本题实际上跟***回溯法经典问题之全排列 第二小题是关系密切的。大家可参考。*** 此题没有说明字符串内的元素是否会有相同的元素,对此,我们当做会有重复的元素,于是我们建立一个bool类型的vector容器used来作为确定每一个节点是否使用过,以此来解决重复插入问题。 接着我们需要加入剪枝操作,以此来解决重复选取问题

一句话概括此题就是:

只有当used[i]==0时才去进行后续操作。

同一树枝上可以选取,但是同一树层上不可以选取!

{i>0&&s[i-1]==s[i]&&used[i-1]==0} 才去进行后续操作。

复制代码
class Solution {
private:
vector<string> result;

void trackback(string& s,string& path,vector<bool>& used)
{
    if(path.size()==s.size())
    {
        result.push_back(path);
        return;
    }

    for(int i=0;i<s.size();i++)
    {
        if(i>0&&s[i]==s[i-1]&&used[i-1]==0)
        continue;

        if (used[i] != 1)
            {
                path.push_back(s[i]);
                used[i] = 1;
                trackback(s,path,used);
                used[i] = 0;
                path.pop_back();
            }
    }
}
public:
    vector<string> permutation(string s) {
        if(s.size()==0)
            return{};
        result.clear();
        vector<bool> used;
        sort(s.begin(),s.end());//关键一步,由于不知道是否重复,所以必须要排序,以找到重复的字母
        string path="";
        used.resize(s.size());
        trackback(s,path,used);
        return result;
    }
};

++特别注意!!! 这里的sort操作是关键的一步!由于不知道是否重复,所以必须要排序,以找到重复的字母,让他们相邻排列。++


🌺二、字母大小写全排列

题目链接:784. 字母大小写全排列

题目描述:

给定一个字符串 s ,通过将字符串 s 中的每个字母转变大小写,我们可以获得一个新的字符串。返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。

示例 1:

复制代码
输入:s = "a1b2"
输出:["a1b2", "a1B2", "A1b2", "A1B2"]

示例 2:

复制代码
输入: s = "3z4"
输出: ["3z4","3Z4"]

提示:

  • 1 <= s.length <= 12
  • s 由小写英文字母、大写英文字母和数字组成

本题思路:

同样的, 采用回溯三部曲,但是我们这次不采用for循环遍历,因为根据题意,本题仅仅只是改变"字母的大小写转化"。如下:

1、定义两个全局变量,一个用来存放符合条件单一结果(path),一个用来存放符合条件结果的集合(result)。注意要记得用一个变量来记录遍历的位置。

2、回溯的主体,回溯终止条件。path保存一组数据,每次遍历到叶子节点,再插入到result中,并且回溯到上一个节点。

3、无需for循环横向遍历,仅仅纵向遍历,即递归的过程。

梳理一下判断的条件:判断是否为字母,如果是字母,则不管他是否为大小写,直接转化为小写插入path,进行递归,递归回来后再转化为大写插入path,递归。如果不是字母,则直接插入parh,进行下一层的递归。

一图让你了解~(以a1b2为例)

cpp 复制代码
class Solution {
private:
vector<string> result;

void backtrack(string& s,string& path,int index)
{
    if(s.size()==path.size())
    {
        result.push_back(path);
        return;
    }
    if(isalpha(s[index]))
    {
        path.push_back(tolower(s[index]));
        backtrack(s,path,index+1);
        path.pop_back();
        path.push_back(toupper(s[index]));
        backtrack(s,path,index+1);
        path.pop_back();
    }else
    {
        path.push_back(s[index]);
        backtrack(s,path,index+1);
        path.pop_back();
    }
 
}
public:
    vector<string> letterCasePermutation(string s) {
        result.clear();
        string path="";
        backtrack(s,path,0);
        return result;
    }
};

感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o!

给个三连再走嘛~

相关推荐
Pocker_Spades_A25 分钟前
Python快速入门专业版(二十六):Python函数基础:定义、调用与返回值(Hello函数案例)
开发语言·python
island131433 分钟前
【C++框架#5】Elasticsearch 安装和使用
开发语言·c++·elasticsearch
周周记笔记1 小时前
学习笔记:Python的起源
开发语言·python
岁忧1 小时前
(LeetCode 每日一题) 3541. 找到频率最高的元音和辅音 (哈希表)
java·c++·算法·leetcode·go·散列表
懒大王95271 小时前
uni-app + Vue3 + EZUIKit.js 播放视频流
开发语言·javascript·uni-app
_extraordinary_1 小时前
Java 多线程进阶(四)-- 锁策略,CAS,synchronized的原理,JUC当中常见的类
java·开发语言
pusue_the_sun1 小时前
每日算法题推送
算法·双指针
JasmineX-11 小时前
数据结构——顺序表(c语言笔记)
c语言·开发语言·数据结构·笔记
KyollBM2 小时前
【Luogu】P9809 [SHOI2006] 作业 Homework (根号算法)
算法
小六子成长记2 小时前
【C++】:list容器全面解析(超详细)
c++·windows·list