LeetCode第131题

难度:中等

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

示例1:

输入:s = "aab"

输出:\["a","a","b","aa","b"]

示例2:

输入:s = "a"

输出:\["a"]

  • 1 <= s.length <= 16

  • s 仅由小写英文字母组成

问题分析

这题让把字符串分割成一些子串,并且要保证每个子串都是回文串,这是一道经典的回溯算法问题,关于回溯算法,可以看下很早之前写的一篇文章《什么叫回溯算法,一看就会,一写就废》。

对原字符串不断的截取子串,如果子串是回文串就继续截取,如果不是回文串就跳过,最后如果都截取完了,说明截取的子串都是回文串,把截取的子串保存下来即可。可以看到子串的截取就像一个 n 叉树一样,如下图所示。

判断子串是否是回文串,我们可以预处理,先计算好哪些子串是回文串哪些不是。如果子串si,j是回文串,需要两边的字符相等si==sj,并且中间的子串si+1,j-1也必须是回文串。

JAVA:

复制代码
public List<List<String>> partition(String s) {
    List<List<String>> ans = new ArrayList<>();
    int length = s.length();
    // 预处理,先计算子串中哪些是回文的,哪些不是,
    // 数组dp[i][j]表示子串s[i,j]是否是回文的。
    boolean[][] dp = new boolean[length][length];
    for (int j = 0; j < length; j++) {
        for (int i = 0; i <= j; i++) {
            // 如果子串s[j,i]是回文串,则两边的字符s[i]和s[j]必须相同,并且
            // 中间的子串s[i+1,j-1]如果存在,也必须是回文串。
            if (s.charAt(i) == s.charAt(j) && (j - i <= 2 || dp[i + 1][j - 1]))
                dp[i][j] = true;
        }
    }

    backTrack(s, dp, 0, ans, new ArrayList<>());// 回溯算法
    return ans;
}

private void backTrack(String s, boolean[][] dp, int index, List<List<String>> ans, List<String> path) {
    // 边界条件判断,字符串s中的字符都访问完了
    if (index >= s.length()) {
        ans.add(new ArrayList<>(path));
        return;
    }
    for (int i = index; i < s.length(); i++) {
        // 如果当前截取的子串不是回文的,就跳过
        if (!dp[index][i]) continue;
        // 这里截取的子串s[index][i]就是回文串。
        path.add(s.substring(index, i + 1));// 做出选择
        backTrack(s, dp, i + 1, ans, path); // 递归
        path.remove(path.size() - 1);// 撤销选择
    }
}

C++:

复制代码
public:
    vector<vector<string>> partition(string s) {
        vector<vector<string>> ans;
        int length = s.length();
        // 预处理,先计算子串中哪些是回文的,哪些不是,
        // 数组dp[i][j]表示子串s[i,j]是否是回文的。
        vector<vector<bool>> dp(length, vector<bool>(length, false));
        for (int j = 0; j < length; j++) {
            for (int i = 0; i <= j; i++) {
                // 如果子串s[j,i]是回文串,则两边的字符s[i]和s[j]必须相同,并且
                // 中间的子串s[i+1,j-1]如果存在,也必须是回文串。
                if (s[i] == s[j] && (j - i <= 2 || dp[i + 1][j - 1]))
                    dp[i][j] = true;
            }
        }
        vector<string> path{};
        backTrack(s, dp, 0, ans, path);// 回溯算法
        return ans;
    }

    void backTrack(string &s, vector<vector<bool>> &dp, int index,
                   vector<vector<string>> &ans, vector<string> &path) {
        // 边界条件判断,字符串s中的字符都访问完了
        if (index >= s.length()) {
            ans.push_back(path);
            return;
        }
        for (int i = index; i < s.length(); i++) {
            // 如果当前截取的子串不是回文的,就跳过
            if (!dp[index][i])
                continue;
            // 这里截取的子串s[index][i]就是回文串。
            path.push_back(s.substr(index, i - index + 1));// 做出选择
            backTrack(s, dp, i + 1, ans, path); // 递归
            path.pop_back();// 撤销选择
        }
    }

Python:

复制代码
def partition(self, s: str) -> List[List[str]]:
    def backTrack(index):
        # 边界条件判断,字符串s中的字符都访问完了
        if index >= length:
            ans.append(path[:])
            return
        for i in range(index, length):
            # 如果当前截取的子串不是回文的,就跳过
            if not dp[index][i]:
                continue
            # 这里截取的子串s[index][i]就是回文串。
            path.append(s[index: i + 1])  # 做出选择
            backTrack(i + 1)  # 递归
            path.pop()  # 撤销选择

    ans = []
    path = []
    length = len(s)
    # 预处理,先计算子串中哪些是回文的,哪些不是,
    # 数组dp[i][j]表示子串s[i,j]是否是回文的。
    dp = [[False] * length for _ in range(length)]
    for j in range(length):
        for i in range(j + 1):
            # 如果子串s[i,j]是回文串,则两边的字符s[i]和s[j]必须相同,并且
            # 中间的子串s[i+1,j-1]如果存在,也必须是回文串。
            if s[i] == s[j] and (j - i <= 2 or dp[i + 1][j - 1]):
                dp[i][j] = True
    backTrack(0)  # 回溯算法
    return ans
相关推荐
Tisfy1 小时前
LeetCode 2333.最小差值平方和:排序 + 数学计算(不用二分)
数学·算法·leetcode·题解·贪心·排序
aqiu1111111 小时前
【C++算法打怪专栏】LeetCode 217. 存在重复元素(解法总结)
c++·算法·leetcode
yi0113 小时前
DAY28: LeetCode 111. 二叉树的最小深度
人工智能·笔记·python·算法·leetcode·二叉树·递归
wuyk5553 小时前
27.LeetCode 2265. 统计值等于子树平均值的节点数 —— 后序遍历实战
stm32·单片机·嵌入式硬件·算法
xiangyun613 小时前
C++ list 模拟实现(二):const迭代器与反向迭代器,模板的黑魔法
开发语言·数据库·c++·算法·stl·list
xxwl5853 小时前
三、区域赛算法模版第三章---字符串相关算法
笔记·学习·算法
信奥卷王3 小时前
[GESP202509 六级] 划分字符串(含视频)
数据结构·算法
酒醉的胡铁4 小时前
相似度搜索的常见算法
算法·ai
局部有解4 小时前
Leetcode Hot100 - 最长连续序列
算法·leetcode·职场和发展
行者全栈架构师4 小时前
给开源 Rust 工具加一个新检查项,TRAE 帮我从想法跑到构建产物:全链路实战
前端·算法·架构