【算法修炼】分割回文串、分割回文串Ⅱ

分割回文串(中等)

在这里插入图片描述
DFS搜索:
先考虑最朴素的DFS搜索,需要把原串拆分成若干子串,关键点就在于:拆分点的选取,那么我们只需要去搜索拆分点即可。

class Solution {
    LinkedList<String> tmp = new LinkedList<>();
    List<List<String>> ans = new LinkedList<>();
    public List<List<String>> partition(String s) {
        dfs(s, 0, 0);
        return ans;
    }
    void dfs(String s, int start, int len) {
        if (len > s.length()) return;
        if (len == s.length()) {
            ans.add(new LinkedList(tmp));
            return;
        }
        // 枚举拆分点
        for (int i = start; i < s.length(); i++) {
            // 枚举所有可能子串
            String str = s.substring(start, i + 1);
            if (check(str)) {
                tmp.add(str);
                dfs(s, i + 1, len + str.length());
                // 回溯
                tmp.removeLast();
            }
        }
    }
    boolean check(String s) {
        int n = s.length();
        if (n <= 1) return true;
        for (int i = 0; i < n / 2; i++) {
            if (s.charAt(i) != s.charAt(n - i - 1)) return false;
        }
        return true;
    }
}

发现:每一次枚举子串时,都需要对其回文性质进行判断,考虑之前DP专题中学到的回文判断dp方法,可以先预处理得到s串中可能的回文串,再进行搜索。

class Solution {
    boolean[][] dp;
    LinkedList<String> tmp = new LinkedList<>();
    List<List<String>> ans = new LinkedList<>();
    public List<List<String>> partition(String s) {
        int n = s.length();
        dp = new boolean[n][n];
        // 预处理s串是否为回文串
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (s.charAt(i) == s.charAt(j)) {
                    if (j == i || j - i == 1) dp[i][j] = true;
                    else dp[i][j] = dp[i + 1][j - 1] || dp[i][j];
                }
            }
        }
        // 枚举左端点
        dfs(s, 0);
        return ans;
    }
    // 在左端点的基础上,枚举右端点
    void dfs(String s, int i) {
        if (i > s.length()) return;
        if (i == s.length()) {
            // 拆分完了整个串
            ans.add(new LinkedList(tmp));
            return;
        }
        for (int j = i; j < s.length(); j++) {
            if (dp[i][j]) {
                // s[i..j]子串是回文串
                tmp.add(s.substring(i, j + 1));
                // 枚举下一个开始点
                dfs(s, j + 1);
                // 回溯,不选取当前的回文子串
                tmp.removeLast();
            }
        }
    }
}

分割回文串Ⅱ(困难)

在这里插入图片描述
dp[i],表示s[0…i]的最少拆分次数,如果s[0…i]就是回文,那么就不需要拆分,次数=0。如果不是回文,可以站在最后一个回文子串的角度进行思考,枚举s[0…i]的最后一个成立的回文子串,它们的拆分点是不同的,确定了最后一个成立的回文子串,再去看前面剩余的字符串的拆分次数即可。

class Solution {
    public int minCut(String s) {
        int n = s.length();
        // 预处理s中的回文子串
        boolean[][] is = new boolean[n][n];
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (s.charAt(i) == s.charAt(j)) {
                    if (j == i || j - i == 1) {
                        is[i][j] = true;
                    } else {
                        is[i][j] = is[i][j] || is[i + 1][j - 1];
                    }
                }
            }
        }
        int[] dp = new int[n + 1];
        Arrays.fill(dp, 0x3f3f3f3f);
        for (int i = 0; i < n; i++) {
            if (is[0][i]) {
                // 如果s[0..i]本来就是回文,那就无需进行分割
                dp[i] = 0;
            } else {
                // 如果s[0..i]不是回文,那就需要思考最后一个回文子串的可能情况
                // j=0的位置就不用考虑
                for (int j = 0; j < i; j++) {
                    if (is[j + 1][i]) {  // 最后j == i代表单独把第i个字符拆出来
                        // s[j+1..i]是回文
                        // 拆分点是s[j],还要看dp[j]的拆分情况
                        dp[i] = Math.min(dp[i], dp[j] + 1);
                    }
                }
            }
        }
        return dp[n - 1];
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值