Leetcode 131-分割回文串

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

回文串 是正着读和反着读都一样的字符串。
在这里插入图片描述

题解

题解转载自liweiwei1419
思路分析: 找到所有可能的解,提示我们可以使用「回溯算法」(采用深度优先遍历的方式遍历一棵隐式树结构)。

方法一:回溯算法

回溯算法思考的步骤:

  • 画出树型结构,本题的递归树模型是一棵多叉树;
    在这里插入图片描述
  1. 编码。
  • 每一个结点表示剩余没有扫描到的字符串,产生分支是截取了剩余字符串的前缀;
  • 产生前缀字符串的时候,判断前缀字符串是否是回文。
  • 如果前缀字符串是回文,则可以产生分支和结点;如果前缀字符串不是回文,则不产生分支和结点,这一步是剪枝操作。
  • 在叶子结点是空字符串的时候结算,此时 从根结点到叶子结点的路径,就是结果集里的一个结果,使用深度优先遍历,记录下所有可能的结果。
  • 使用一个路径变量 path 搜索,path 全局使用一个(注意结算的时候,要生成一个拷贝),因此在递归执行方法结束以后需要回溯,即将递归之前添加进来的元素拿出去;
  • path 的操作只在列表的末端,因此合适的数据结构是栈。
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

public class Solution {

    public List<List<String>> partition(String s) {
        int len = s.length();
        List<List<String>> res = new ArrayList<>();
        if (len == 0) {
            return res;
        }

        // Stack 这个类 Java 的文档里推荐写成 Deque<Integer> stack = new ArrayDeque<Integer>();
        // 注意:只使用 stack 相关的接口
        Deque<String> stack = new ArrayDeque<>();
        char[] charArray = s.toCharArray();
        dfs(charArray, 0, len, stack, res);
        return res;
    }

    /**
     * @param charArray
     * @param index     起始字符的索引
     * @param len       字符串 s 的长度,可以设置为全局变量
     * @param path      记录从根结点到叶子结点的路径
     * @param res       记录所有的结果
     */
    private void dfs(char[] charArray, int index, int len, Deque<String> path, List<List<String>> res) {
        if (index == len) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = index; i < len; i++) {
            // 因为截取字符串是消耗性能的,因此,采用传子串下标的方式判断一个子串是否是回文子串
            if (!checkPalindrome(charArray, index, i)) {
                continue;
            }
            path.addLast(new String(charArray, index, i + 1 - index));
            dfs(charArray, i + 1, len, path, res);
            path.removeLast();
        }
    }

    /**
     * 这一步的时间复杂度是 O(N),优化的解法是,先采用动态规划,把回文子串的结果记录在一个表格里
     *
     * @param charArray
     * @param left      子串的左边界,可以取到
     * @param right     子串的右边界,可以取到
     * @return
     */
    private boolean checkPalindrome(char[] charArray, int left, int right) {
        while (left < right) {
            if (charArray[left] != charArray[right]) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
}

在这里插入图片描述

方法二:回溯的优化(使用动态规划得到所有子串是否是回文)

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

public class Solution {

    public List<List<String>> partition(String s) {
        int len = s.length();
        List<List<String>> res = new ArrayList<>();
        if (len == 0) {
            return res;
        }

        char[] charArray = s.toCharArray();
        // 预处理
        // 状态:dp[i][j] 表示 s[i][j] 是否是回文
        boolean[][] dp = new boolean[len][len];
        // 状态转移方程:在 s[i] == s[j] 的时候,dp[i][j] 参考 dp[i + 1][j - 1]
        for (int right = 0; right < len; right++) {
            // 注意:left <= right 取等号表示 1 个字符的时候也需要判断
            for (int left = 0; left <= right; left++) {
                if (charArray[left] == charArray[right] && (right - left <= 2 || dp[left + 1][right - 1])) {
                    dp[left][right] = true;
                }
            }
        }

        Deque<String> stack = new ArrayDeque<>();
        dfs(s, 0, len, dp, stack, res);
        return res;
    }

    private void dfs(String s, int index, int len, boolean[][] dp, Deque<String> path, List<List<String>> res) {
        if (index == len) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = index; i < len; i++) {
            if (dp[index][i]) {
                path.addLast(s.substring(index, i + 1));
                dfs(s, i + 1, len, dp, path, res);
                path.removeLast();
            }
        }
    }
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划方法可以用来解决分割文串的问题。可以根据给定的字符串s,使用动态规划找到所有可能的文子串。 首先,我们可以定义一个二维数组dp,其中dp[i][j]表示字符串s从索引i到j的子串是否是文串。对于任意的i和j,如果s的第i个字符和第j个字符相等,并且s的第i+1个字符到第j-1个字符是文串,则dp[i][j]为true。 然后,我们可以使用动态规划填充dp数组。我们可以从字符串s的末尾开始遍历,每次遍历一个字符。对于每个索引i,我们再从i开始向右遍历,直到字符串的末尾。对于每个索引i和j,我们检查字符串s从索引i到j是否是文串。如果是文串,则将这个子串添加到结果集中,并继续向右遍历,搜索下一个可能的文子串。 最后,当我们遍历完整个字符串s时,我们就可以得到所有可能的分割方案。每个分割方案都是由一组文子串组成的。 下面是一个使用动态规划的实现示例代码: ```cpp class Solution { public: vector<vector<string>> partition(string s) { vector<vector<string>> result; vector<string> path; vector<vector<bool>> dp(s.size(), vector<bool>(s.size(), false)); backtracking(s, 0, path, result, dp); return result; } void backtracking(string const& s, int startIndex, vector<string>& path, vector<vector<string>>& result, vector<vector<bool>>& dp) { if (startIndex >= s.size()) { result.push_back(path); return; } for (int i = startIndex; i < s.size(); i++) { if (s[startIndex == s[i && (i - startIndex <= 2 || dp[startIndex + 1][i - 1])) { dp[startIndex][i = true; path.push_back(s.substr(startIndex, i - startIndex + 1)); backtracking(s, i + 1, path, result, dp); path.pop_back(); } } } }; ``` 这是一个基于溯和动态规划的算法,它可以找到字符串s所有可能的分割方案,使得每个子串都是文串。你可以根据自己的需求使用这个算法来解决分割文串的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [131. 分割文串 溯 c++](https://blog.csdn.net/qq_39993896/article/details/127132759)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [LeetCode-分割文串(C++)](https://blog.csdn.net/weixin_42817333/article/details/125468202)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值