分割回文串
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 和 . 组成的字符串列表。
