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

LeetCode 热题 100_分割回文串(61_131_中等_C++)(递归(回溯))(回溯问题使用类成员变量还是函数传参)

LeetCode 热题 100_分割回文串(61_131)

    • 题目描述:
    • 输入输出样例:
    • 题解:
      • 解题思路:
        • 思路一(递归(回溯)):
      • 代码实现
        • 代码实现(思路一(递归(回溯))):
        • 以思路一为例进行调试
        • 部分代码解读

题目描述:

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是
回文串 。返回 s 所有可能的分割方案。

输入输出样例:

示例 1:
输入:s = “aab”
输出:[[“a”,“a”,“b”],[“aa”,“b”]]

示例 2:
输入:s = “a”
输出:[[“a”]]

提示:
1 <= s.length <= 16
s 仅由小写英文字母组成

题解:

解题思路:

思路一(递归(回溯)):

1、题目要求将 s 分割成一些子串,使每个子串都是 回文串。所以就转换为了如何切割 s ,并判断切割的每个字串。
示例1中 :s = “aab”,其可切割的地方为 ”a|a|b|“,| 为可以切割的位置。 “|aab” 此种切割方式是不可以的,因 ”“ 不为回文串。 “aab|” 的切割方式是可以的。
我们选取 ”a|a|b|“ 中 | 的各种组合,形成不同的切割方式,递归树如下图所示。
在这里插入图片描述

2、复杂度分析:
① 时间复杂度:O(n⋅2 n),其中n是字符串s的长度。在最坏情况下,s包含n个完全相同的字符,因此它的任意一种划分方法都满足要求。
② 空间复杂度:O(n),这里不计算返回答案占用的空间。主要取决于path的大小 O(n) 和递归的深度O(n)。

代码实现

代码实现(思路一(递归(回溯))):
class Solution {
private:
    vector<vector<string>> ans; // 存储所有回文分割结果
    vector<string> path;        // 存储当前正在构建的分割方案

    // 回溯函数,用于探索所有可能的回文分割
    void backtracking(const string& s, int startIndex) {
        // 如果起始位置已经大于或等于字符串的长度,说明已经找到了一组有效的分割方案
        if (startIndex >= s.size()) {
            ans.emplace_back(path); // 将当前的分割方案添加到结果集
            return;
        }

        // 尝试所有从startIndex开始的子串(i看成"|"的位置)
        for (int i = startIndex; i < s.size(); i++) {
            // 判断子串s[startIndex...i]是否是回文
            if (isPalindrome(s, startIndex, i)) {
                // 如果是回文,将该子串添加到当前路径
                path.emplace_back(s.substr(startIndex, i - startIndex + 1));
                
                // 继续回溯,探索下一个可能的回文子串
                backtracking(s, i + 1); // 从i + 1位置开始寻找下一个子串
                
                // 回溯:弹出当前已添加的子串,继续尝试其他可能的分割
                path.pop_back();
            }
        }
    }

    // 辅助函数,判断子串s[left...right]是否是回文
    bool isPalindrome(const string& s, int left, int right) {
        // 从两端向中间检查字符是否相同
        while (left < right) {
            if (s[left++] != s[right--]) return false; // 如果不相同,返回false
        }
        return true; // 如果没有不相同的字符,说明是回文
    }

public:
    // 主函数,将字符串s分割成所有可能的回文子串组合
    vector<vector<string>> partition(string s) {
        ans.clear();  // 清空之前的结果
        path.clear(); // 清空当前的路径(从头开始)

        backtracking(s, 0); // 从索引0开始进行回溯

        return ans; // 返回包含所有回文分割的结果
    }
};
以思路一为例进行调试
#include<iostream>
#include <vector>
using namespace std;


class Solution {
private:
    vector<vector<string>> ans; // 存储所有回文分割结果
    vector<string> path;        // 存储当前正在构建的分割方案

    // 回溯函数,用于探索所有可能的回文分割
    void backtracking(const string& s, int startIndex) {
        // 如果起始位置已经大于或等于字符串的长度,说明已经找到了一组有效的分割方案
        if (startIndex >= s.size()) {
            ans.emplace_back(path); // 将当前的分割方案添加到结果集
            return;
        }

        // 尝试所有从startIndex开始的子串
        for (int i = startIndex; i < s.size(); i++) {
            // 判断子串s[startIndex...i]是否是回文
            if (isPalindrome(s, startIndex, i)) {
                // 如果是回文,将该子串添加到当前路径
                path.emplace_back(s.substr(startIndex, i - startIndex + 1));
                
                // 继续回溯,探索下一个可能的回文子串
                backtracking(s, i + 1); // 从i + 1位置开始寻找下一个子串
                
                // 回溯:弹出当前已添加的子串,继续尝试其他可能的分割
                path.pop_back();
            }
        }
    }

    // 辅助函数,判断子串s[left...right]是否是回文
    bool isPalindrome(const string& s, int left, int right) {
        // 从两端向中间检查字符是否相同
        while (left < right) {
            if (s[left++] != s[right--]) return false; // 如果不相同,返回false
        }
        return true; // 如果没有不相同的字符,说明是回文
    }

public:
    // 主函数,将字符串s分割成所有可能的回文子串组合
    vector<vector<string>> partition(string s) {
        ans.clear();  // 清空之前的结果
        path.clear(); // 清空当前的路径(从头开始)

        backtracking(s, 0); // 从索引0开始进行回溯

        return ans; // 返回包含所有回文分割的结果
    }
};
    

int main(int argc, char const *argv[])
{
    // 定义一个字符串 "aab"
    string str = "aab";
    
    // 创建Solution类的对象s
    Solution s;
    
    // 调用partition方法,传入字符串str,获取所有的回文分割方案
    vector<vector<string>> ans = s.partition(str);
    
    // 遍历回文分割结果ans
    for (int i = 0; i < ans.size(); i++)
    {
        // 输出每个回文分割的开始 "["
        cout << "[";
        
        // 遍历当前回文分割方案中的每个回文子串
        for (int j = 0; j < ans[i].size(); j++)
        {
            // 输出当前的回文子串
            cout << ans[i][j];
            
            // 如果不是当前回文分割方案中的最后一个子串,输出逗号 ","
            if (j != ans[i].size() - 1)
            {
                cout << ",";
            }
        }
        
        // 输出每个回文分割的结束 "]"
        cout << "]";
    }
    
    return 0;  // 程序结束
}
部分代码解读

回溯问题使用类成员变量还是函数传参
对于这个特定的回溯问题,使用类成员变量(全局变量)更常见的选择,因为:
回溯过程中,result 和 path 是不断变化并传递的状态,作为类的成员变量可以方便地共享这些状态。
isPalindrome 在回溯过程中是一个固定的数据结构,可以在computePalindrome 中事先计算好并作为成员变量保存,避免重复计算。
但如果你考虑代码的灵活性和将来可能的重构(比如支持多线程或处理多个字符串),将这些变量作为函数参数传递可能会更清晰和模块化。
因此,在你当前的实现中,保留它们作为类的成员变量是一个合理的选择。如果你希望代码更加灵活、可重用,可以考虑将它们作为参数传递给函数。

LeetCode 热题 100_分割回文串(61_131)原题链接
欢迎大家和我沟通交流(✿◠‿◠)


http://www.kler.cn/a/551600.html

相关文章:

  • 人工智能基础之数学基础:01高等数学基础
  • 小米电视维修记录 2025/2/18
  • Spark ,虚拟机基本命令(1)
  • 【Modelsim】warning:(vsim-WLF-5000) WLF file currently in use:vsim.wlf
  • R语言用逻辑回归贝叶斯层次对本垒打数据与心脏移植数据后验预测检验模拟推断及先验影响分析|附数据代码...
  • Mysql-事务日志undo log
  • React实现自动滚动表格
  • 【Python】实时将数据写入Excel
  • 分布式同步锁:原理、实现与应用
  • 国产编辑器EverEdit - 自动完成功能的用法
  • 蓝桥杯单片机基础部分——单片机介绍部分
  • 安装torch-geometric库,踩坑!
  • Ubuntu Linux运维实战指南4_文件系统基础知识
  • git自动化之.netrc配置
  • 【云安全】云原生- K8S 污点横移
  • 算法与数据结构(子集)
  • 深入解析 iOS 视频录制(三):完整录制流程的实现与整合
  • 基于SpringBoot的个人学习记录平台的设计
  • flash attention
  • k8s集群如何赋权普通用户仅管理指定命名空间资源