LeetCode 热题 100 之 131. 分割回文串 51. N 皇后

  1. 分割回文串

  2. N 皇后

131. 分割回文串

复制代码
class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> res = new ArrayList<>();
        List<String> path = new ArrayList<>();
        backtrack(s, 0, path, res);
        return res;
    }

    // start:当前要分割的起始位置
    private void backtrack(String s, int start, List<String> path, List<List<String>> res) {
        // 终止条件:起始位置等于字符串长度,说明已完成一次有效分割
        if (start == s.length()) {
            res.add(new ArrayList<>(path));
            return;
        }
        // 枚举从 start 开始的所有可能子串结束位置
        for (int end = start; end < s.length(); end++) {
            // 判断子串 s[start..end] 是否为回文
            if (isPalindrome(s, start, end)) {
                path.add(s.substring(start, end + 1)); // 加入回文子串
                backtrack(s, end + 1, path, res);     // 递归分割剩余部分
                path.remove(path.size() - 1);         // 回溯:撤销选择
            }
        }
    }

    // 判断 s[left..right] 是否为回文串
    private boolean isPalindrome(String s, int left, int right) {
        while (left < right) {
            if (s.charAt(left) != s.charAt(right)) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
}
解题思路1:回溯法 + 回文判断

回溯核心逻辑

  • start 表示当前分割的起始位置,当 start == s.length() 时,说明已将整个字符串分割为回文子串,将当前路径加入结果集。

  • 遍历从 start 到末尾的所有 end,若 s[start..end] 是回文,则将其加入路径,递归处理 end+1 位置,之后回溯撤销选择。

回文判断 isPalindrome:双指针法,从两端向中间遍历,若所有对称位置字符都相等则为回文,时间复杂度 O(n)。

优化提示:可预处理回文子串(动态规划),将回文判断优化为 O(1),提升效率。

复制代码
import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> res = new ArrayList<>();
        List<String> path = new ArrayList<>();
        
        // 第一步:DP 预处理所有回文子串 O(1) 查询
        boolean[][] dp = getPalindromeDP(s);
        
        // 第二步:回溯枚举所有分割方案
        backtrack(s, 0, path, res, dp);
        return res;
    }

    // 回溯:从 start 位置开始分割
    private void backtrack(String s, int start, List<String> path, List<List<String>> res, boolean[][] dp) {
        // 递归终止:整个字符串分割完成
        if (start == s.length()) {
            res.add(new ArrayList<>(path));
            return;
        }

        // 枚举所有结束位置 end
        for (int end = start; end < s.length(); end++) {
            // O(1) 判断是不是回文
            if (dp[start][end]) {
                path.add(s.substring(start, end + 1));
                backtrack(s, end + 1, path, res, dp);
                path.remove(path.size() - 1); // 回溯
            }
        }
    }

    // DP 预处理:生成回文表
    private boolean[][] getPalindromeDP(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n];

        // 从短串 → 长串递推
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (i == j) {
                    dp[i][j] = true; // 单个字符一定是回文
                } else if (j == i + 1) {
                    dp[i][j] = s.charAt(i) == s.charAt(j); // 两个字符
                } else {
                    dp[i][j] = (s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]);
                }
            }
        }
        return dp;
    }
}
解题思路2:回溯 + DP 预处理回文

51. N 皇后

复制代码
class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> res = new ArrayList<>();
        // 用数组记录每行皇后所在列:row[i] = j 表示第 i 行皇后在第 j 列
        int[] row = new int[n];
        // 用三个布尔数组分别标记列、主斜线、副斜线是否被占用
        boolean[] usedCol = new boolean[n];
        boolean[] usedDiag1 = new boolean[2 * n - 1]; // row - col + (n-1) 映射到 0~2n-2
        boolean[] usedDiag2 = new boolean[2 * n - 1]; // row + col 映射到 0~2n-2

        backtrack(0, n, row, usedCol, usedDiag1, usedDiag2, res);
        return res;
    }

    // row:当前处理到第几行
    private void backtrack(int row, int n, int[] rowQueen, boolean[] usedCol,
                           boolean[] usedDiag1, boolean[] usedDiag2, List<List<String>> res) {
        // 终止条件:所有行处理完毕,生成棋盘
        if (row == n) {
            res.add(generateBoard(rowQueen, n));
            return;
        }
        // 尝试在当前行的每一列放置皇后
        for (int col = 0; col < n; col++) {
            int diag1 = row - col + (n - 1); // 主斜线映射
            int diag2 = row + col;           // 副斜线映射
            // 检查列、主斜线、副斜线是否冲突
            if (!usedCol[col] && !usedDiag1[diag1] && !usedDiag2[diag2]) {
                // 放置皇后
                rowQueen[row] = col;
                usedCol[col] = true;
                usedDiag1[diag1] = true;
                usedDiag2[diag2] = true;
                // 递归处理下一行
                backtrack(row + 1, n, rowQueen, usedCol, usedDiag1, usedDiag2, res);
                // 回溯:撤销选择
                usedCol[col] = false;
                usedDiag1[diag1] = false;
                usedDiag2[diag2] = false;
            }
        }
    }

    // 根据 rowQueen 生成题目要求的棋盘格式
    private List<String> generateBoard(int[] rowQueen, int n) {
        List<String> board = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            char[] line = new char[n];
            for (int j = 0; j < n; j++) {
                line[j] = (rowQueen[i] == j) ? 'Q' : '.';
            }
            board.add(new String(line));
        }
        return board;
    }
}
解题思路:回溯法 + 位 / 集合约束检查

约束标记

  • usedCol[]:标记某列是否已有皇后

  • usedDiag1[]:标记主斜线(row - col 方向)是否被占用,通过 row - col + (n-1) 映射到非负索引

  • usedDiag2[]:标记副斜线(row + col 方向)是否被占用

回溯逻辑

  • 逐行处理,在当前行尝试所有合法列位置

  • 若位置合法则放置皇后,递归处理下一行

  • 递归结束后回溯,撤销当前皇后的占用标记

棋盘生成 :根据 rowQueen 数组逐行构建 Q. 组成的字符串列表。

相关推荐
Peter·Pan爱编程13 小时前
23. 算法库:用算法代替手写循环
c++·人工智能·算法
小欣加油13 小时前
leetcode2161 根据给定数字划分数组
数据结构·c++·算法·leetcode·职场和发展
Momo__zz14 小时前
零代码平台设计
算法·深度优先
cpp_250114 小时前
P2947 [USACO09MAR] Look Up S
数据结构·c++·算法·题解·单调栈·洛谷
水木流年追梦14 小时前
大模型入门-大模型优化方法13- MTP 多 token 输出、DCA 双块注意力
人工智能·分布式·算法·正则表达式·prompt
数据皮皮侠14 小时前
全国消协智慧 315 平台投诉信息数据库
大数据·人工智能·算法·百度·制造
sbjdhjd14 小时前
面试题完结 | 投票题 + 到岗时间 + 压力缓解
经验分享·笔记·面试·职场和发展·开源·求职招聘·印象笔记
8Qi815 小时前
LeetCode 115 & 392:不同子序列 / 判断子序列
算法·leetcode·职场和发展·动态规划
小蒋学算法15 小时前
算法-乘法表中第K小的数-二分
数据结构·算法
智者知已应修善业15 小时前
【51单片机8个LED,已经使用了D1D2,怎么样在不动D1D2的前提下实现D6~D8的流水灯】2024-1-19
c++·经验分享·笔记·算法·51单片机