(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];
    }
}

思路

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

相关推荐
地平线开发者8 小时前
J6B vio scenario sample
算法
BothSavage20 小时前
Trae远程开发中DeepSeek自定义模型4054错误的排查与修复
算法
小林ixn20 小时前
从暴力到KMP:一道题彻底搞懂字符串匹配的前世今生
算法
烬羽1 天前
字符串算法入门:从反转字符串到回文判断,面试不再慌
算法·面试
先吃饱再说2 天前
判断回文字符串,从一行代码到双指针优化
算法
黄敬峰2 天前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术2 天前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六2 天前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程