【leetcode】分割回文子字符串

0、参考资料

动态规划解决回文子串

一、题目描述

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

回文串 是正着读和反着读都一样的字符串。

输入:s = “google”
输出:[[“g”,“o”,“o”,“g”,“l”,“e”],[“g”,“oo”,“g”,“l”,“e”],[“goog”,“l”,“e”]]

二、代码思路

这题第一眼看,发现很好处理。

枚举出所有子字符串是回文串然后组合即可。

枚举出所有子字符串确实很简单,但是组合你如何处理呢?传统的暴力很难解决的,只能从算法和数据结构上找比较好的解法。

所以大体思路也跟上面差不多,但是细节有很大的出入。

首先,我们需要前置知识如何找出所有的回文子串,这里我们可以使用动态规划的想法,使用一个dp[][]二维数组表,来存储相应的结果。详细思路,看我的上一篇文章。

其次,我们有了标记回文子串的二维数组表,我们如何才能枚举出所有回文子串的组合呢?并且这些组合最终能形成一个完整的字符串。也就是将这个字符串拆成一些回文子串的组合形式。 这里我们就用到了回溯算法:

这里的回溯算法思路是这样的:假如我们有一个字符串“ggag”我们每次都要选一个回文子串,然后基于这个回文子串的选择,再从剩下的串中选择另一个回文子串。直到选完为止。然后退出这一层,并回溯到上一层,观察我选择这个子串之后还有另一种选择的可能吗?其实有点不好描述,结合代码和图应该会很好理解。

在这里插入图片描述
因为每一层都是从当前pos出发,让i一直走,这样就能知道从pos开始的所有子串中的所有回文串;同理,从第二个出发再找。如此回溯。

三、代码题解
class Solution {
    private List<String> path = new ArrayList();
    private List<List<String>> res = new ArrayList();
    private String s;
    private int n;
    private int dp[][];
    public String[][] partition(String s) {
        //暴力解法:
        //选出所有子字符串,然后逐个判断。
        //回溯法(可以动态地选出所有子串) + 动态规划法(可以很好判断某一个字符串是不是回文串,并且将结果预处理到表格中)
        this.s = s;
        this.n = s.length();
        this.dp = new int[s.length()][s.length()];
        //DP预处理
        dp(s);
        //回溯枚举出所有可能的子串,注意!我这里描述的不准确:这里的回溯不是简单的回溯,和我们之前做的回溯有本质上区别。
        //我们之前的回溯无论是多叉还是二叉都是一个个元素来选择的,但是这里的回溯不是选择单个元素形成一个分叉,而是选择一串子串作为一个分叉,我画个图请看。
        dfs(0);
        String[][] ans = new String[res.size()][];
        for (int i = 0; i < res.size(); i++) {
            ans[i] = res.get(i).toArray(new String[res.get(i).size()]);
        }
        return ans;
    }
    //DP算法
    public void dp(String str) {
        for (int i = 0; i < s.length(); i++) {
            for (int j = 0; j <= i; j++) {
                if ((s.charAt(i) == s.charAt(j)) && (((i - j) < 2) || dp[j + 1][i - 1] == 1)) {
                    dp[j][i] = 1;
                }
            }
        }
        //System.out.println(count);
    }
    //回溯算法
    public void dfs(int cur) {
        if (cur == n) {
            res.add(new ArrayList(path));
            return;
        }
        for (int i = cur; i < n; i++) {
            if (dp[cur][i] == 1) {
                path.add(s.substring(cur, i + 1));
                //注意,这里的回溯是选择回文子串,而并非是选择单个字符。
                //所以,这里并不能是 cur + 1,而应该是i + 1.
                //原因就是:cur - i 已经成为一个子串了,那么证明该字串被选。
                //那么我们就应该从i + 1,开始再去选择下一个子串。
                //这个思想很重要。
                dfs(i + 1);
                path.remove(path.size() - 1);
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值