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. 组成的字符串列表。

相关推荐
进击的小头2 小时前
第21篇:BUCK变换器双环控制系统设计与参数整定调试实战
python·算法
liliangcsdn2 小时前
信息检索评估指标Recall@K的分析和计算示例
算法·全文检索
handsomethefirst2 小时前
【算法与数据结构】【面试经典150题】【题36-题40】
数据结构·算法·面试
庞轩px2 小时前
面试回答第十五问:类加载
jvm·面试·职场和发展·常量池·类加载·字节码·klass
寒月小酒2 小时前
3.29+3.30
数据结构·算法
Flying pigs~~2 小时前
基于Bert的模型迁移文本分类项目
人工智能·深度学习·算法·大模型·nlp·bert
ZoeJoy82 小时前
算法筑基(六):分治算法——大事化小,小事化了
算法·排序算法·动态规划·哈希算法·图搜索算法
阿Y加油吧2 小时前
二叉树面试送分题|力扣101对称+226翻转(递归极简写法,手写无压力)
leetcode·面试·职场和发展
美式请加冰2 小时前
BFS算法(下)
算法·宽度优先