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
相关推荐
To_OC10 小时前
LC 994 腐烂的橘子:人人都说是 BFS 入门题,我却写了三遍才过
javascript·算法·leetcode
金銀銅鐵13 小时前
[Python] 扩展欧几里得算法
python·数学·算法
To_OC16 小时前
LC 200 岛屿数量:经典 DFS 入门题,我第一次写居然连方向都搞错了
javascript·算法·leetcode
To_OC1 天前
LC 128 最长连续序列:别上来就排序,O (n) 解法才是这题的灵魂
javascript·算法·leetcode
05Kevin2 天前
lk每日冒险题--数据结构6.27
算法
To_OC2 天前
从一次栈溢出报错说起,我把递归彻底扒明白了
javascript·算法·程序员
千纸鹤安安3 天前
千问Qwen-AgentWorld来了:一个语言模型搞定七大Agent场景,GPT-5.4都输了
算法
七牛开发者3 天前
MCP 到底是什么?为什么 Agent 都想接上它
算法·aigc·agent