Featured image of post Palindrome Partitioning

Palindrome Partitioning

131. 分割回文串

分析

  1. 预处理判断回文子串:
    • 通过动态规划预处理所有可能的子串,判断每个子串是否是回文
    • 定义二维数组 f[i][j],表示从 ij 的子串是否为回文:
      • s[i] == s[j],且内部的子串 s[i+1:j-1] 也是回文,则 f[i][j] = true
      • 特殊情况下(i == jj == i+1),直接判断字符是否相等
  2. 回溯搜索分割方案:
    • 从字符串的第一个字符开始尝试分割
    • 若当前子串是回文,则将其加入路径中,继续搜索剩余部分
    • 如果遍历完整个字符串,将当前路径保存为一种结果
    • 搜索结束后,将最后加入路径的子串弹出,回溯到上一步继续尝试

时间复杂度

  • 预处理回文数组:需要枚举所有子串,时间复杂度为 O(n²)
  • 回溯搜索:每个字符有两种选择(分割或不分割),复杂度近似为 O(2ⁿ)

总体时间复杂度:O(n² + 2ⁿ)

空间复杂度

  • 动态规划数组 f 占用 O(n²)
  • 递归深度最多为字符串长度 n,路径数组和递归栈占用 O(n)

总体空间复杂度:O(n²)

C++代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Solution
{
public:
    std::vector<std::vector<std::string>> res; // 存储最终结果
    std::vector<std::string> path; // 当前路径
    std::vector<std::vector<bool>> f; // 动态规划存储回文判断结果

    vector<vector<string>> partition(string s)
    {
      int n = s.size();
      // 初始化回文判断数组
      f = std::vector<std::vector<bool>>(n, std::vector<bool>(n));

      // 动态规划预处理回文子串
      for (int j = 0; j < n; ++j)
        for (int i = 0; i <= j; ++i)
        {
          if (i == j) // 单个字符是回文
            f[i][j] = true;
          else if (s[i] == s[j]) // 判断两端字符是否相等
            if (i + 1 > j - 1 || f[i + 1][j - 1]) // 内部子串为空或回文
              f[i][j] = true;
        }

      // 开始回溯搜索
      dfs(s, 0);
      return res;
    }

    void dfs(std::string s, int u)
    {
      // 如果已经分割到字符串末尾,保存当前路径
      if (u == s.size())
      {
        res.push_back(path);
        return;
      }

      // 遍历从 u 开始的每个子串
      for (int i = u; i < s.size(); ++i)
        if (f[u][i]) // 如果当前子串是回文
        {
          path.push_back(s.substr(u, i - u + 1)); // 加入路径
          dfs(s, i + 1); // 递归处理剩余部分
          path.pop_back(); // 回溯
        }
    }
};
Built with Hugo
Theme Stack designed by Jimmy