(leetcode)力扣100 61分割回文串(回溯,动归)

题目

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

数据范围

1 <= s.length <= 16

s 仅由小写英文字母组成

测试用例

示例1

java 复制代码
输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例2

java 复制代码
输入:s = "a"
输出:[["a"]]

题解1(官解1,时间On 2n,空间On2

java 复制代码
class Solution {
    // res 用于存储最终所有可能的分割方案(结果列表)
    List<List<String>> res = new ArrayList<>();
    // curr 用于存储当前回溯路径中已经切分出来的回文子串
    List<String> curr = new ArrayList<>();
    // isPd[i][j] 表示字符串从索引 i 到 j 的子串是否为回文串
    boolean isPd[][];
    int n;

    public List<List<String>> partition(String s) {
        n = s.length();
        // 初始化 DP 表,n+1 是为了防止 i+1 或 j-1 越界时的溢出
        isPd = new boolean[n + 1][n + 1];
        
        // 预处理第一步:填充默认值
        // 将所有位置设为 true,是因为当子串长度为 1 (i==j) 或长度为 0 (i>j) 时,它们逻辑上是回文的基础
        for (int i = 0; i < n; i++) {
            Arrays.fill(isPd[i], true);
        }

        // 预处理第二步:动态规划填表
        // 状态转移方程:s[i...j] 是回文 <=> (s[i] == s[j]) 且 (内部子串 s[i+1...j-1] 也是回文)
        // 注意:i 需要从大到小遍历,因为计算 isPd[i] 需要用到 i+1 的结果
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i + 1; j < n; j++) {
                isPd[i][j] = (s.charAt(i) == s.charAt(j)) && isPd[i + 1][j - 1];
            }
        }

        // 从索引 0 开始进行深度优先搜索(回溯)
        dfs(s, 0);
        return res;
    }

    /**
     * 回溯函数:尝试从 pos 位置开始切分字符串
     */
    public void dfs(String s, int pos) {
        // 递归出口:如果起始位置已经到达字符串末尾,说明找到了一组完整的分割方案
        if (pos == n) {
            // 注意:必须 new 一个新列表存入 res,否则 curr 的变化会影响已存入的结果
            res.add(new ArrayList<String>(curr));
            return;
        }

        // 从当前位置 pos 开始,尝试所有可能的切分点 j
        for (int j = pos; j < n; j++) {
            // 利用预处理好的 DP 表,在 O(1) 时间内判断当前切下的子串 s[pos...j] 是否回文
            if (isPd[pos][j]) {
                // 1. 做选择:将当前回文子串加入路径
                curr.add(s.substring(pos, j + 1));
                
                // 2. 递归:从 j+1 的位置继续向后切分
                dfs(s, j + 1);
                
                // 3. 撤销选择:回溯,移除最后一个切片,尝试下一种切分可能
                curr.remove(curr.size() - 1);
            }
        }
    }
}

题解2(官解2,时空同上)

java 复制代码
class Solution {
    int[][] f;
    List<List<String>> ret = new ArrayList<List<String>>();
    List<String> ans = new ArrayList<String>();
    int n;

    public List<List<String>> partition(String s) {
        n = s.length();
        f = new int[n][n];

        dfs(s, 0);
        return ret;
    }

    public void dfs(String s, int i) {
        if (i == n) {
            ret.add(new ArrayList<String>(ans));
            return;
        }
        for (int j = i; j < n; ++j) {
            if (isPalindrome(s, i, j) == 1) {
                ans.add(s.substring(i, j + 1));
                dfs(s, j + 1);
                ans.remove(ans.size() - 1);
            }
        }
    }

    // 记忆化搜索中,f[i][j] = 0 表示未搜索,1 表示是回文串,-1 表示不是回文串
    public int isPalindrome(String s, int i, int j) {
        if (f[i][j] != 0) {
            return f[i][j];
        }
        if (i >= j) {
            f[i][j] = 1;
        } else if (s.charAt(i) == s.charAt(j)) {
            f[i][j] = isPalindrome(s, i + 1, j - 1);
        } else {
            f[i][j] = -1;
        }
        return f[i][j];
    }
}

思路

这道题稍微比前几道题难一些,主要是考了一些回文的知识,并且考察了如何处理重复计算的方法,本文的题解一是通过动态规划提前预处理,题解二是通过记忆数组,两个方法都可以,动态规划代码简单一些,看个人吧,回溯思想都老生常谈了,就不重复讲解了。

相关推荐
Deep-w14 小时前
【MATLAB】基于 MATLAB 的离网光伏储能微电网容量优化仿真研究
开发语言·算法·matlab
闵孚龙14 小时前
Qwen3.7-Max深度解析:智能体Agent、AI编程、MCP工作流、跨框架泛化与百炼API,一次讲透国产大模型新前沿
人工智能·算法·架构·ai编程
Jasmine_llq14 小时前
《B4261 [GESP202503 三级] 2025》
开发语言·c++·算法·条件判断算法·位运算恒等式推导·简单算术运算
简单点好不好15 小时前
工作中的工程问题: 找圆?
算法
阿Y加油吧15 小时前
两道字符串 DP 模板题复盘:最长公共子序列 & 编辑距离
leetcode
我爱cope15 小时前
【力扣hot100:76. 最小覆盖子串】
算法·leetcode·职场和发展
社交怪人15 小时前
【歌手大奖赛】信息学奥赛一本通C语言解法(题号2072)
c语言·算法
数据科学小丫15 小时前
算法:逻辑回归
算法·机器学习·逻辑回归
爱写代码的小朋友15 小时前
基于多约束遗传算法的中小学排座位优化模型研究
linux·人工智能·算法
один but you15 小时前
unorder_map 和unorder_set
算法·哈希算法·散列表