
本文详解如何用递归生成字符串的所有非空**有序子序列**(即考虑顺序的子集,等价于所有非空排列的并集),修正原始代码逻辑错误,并提供可运行、去重、按长度分组输出的完整解决方案。
原始代码的核心问题在于:它实现了带索引偏移的子集生成(类似组合,不重用已选字符但固定遍历顺序),却未对每个子集进一步做全排列;同时,idx + 1 的递归调用方式导致字符只能按原始顺序拼接(如 "ABC" 只能生成 "A", "AB", "ABC",无法得到 "BA" 或 "CA"),因此输出杂乱且缺失关键序列。
正确思路应分为两步:
- 枚举所有非空子集长度(1 到 n);
- 对每个长度,生成该长度下所有可能的排列(即从 n 个字符中选 k 个并全排列)。
以下为推荐实现——采用「每次选取一个字符,剩余字符递归处理」的经典回溯法,天然支持乱序选择:
import java.util.*;
public class LetterSequences {
public static void main(String[] args) {
String tiles = "ABC";
List result = new ArrayList<>();
solve(tiles, "", result);
// 按长度分组输出,更清晰对应题目示例格式
Map> grouped = new TreeMap<>();
for (String s : result) {
grouped.computeIfAbsent(s.length(), k -> new ArrayList<>()).add(s);
}
for (int len : grouped.keySet()) {
System.out.println(String.join(",", grouped.get(len)));
}
}
public static void solve(String remaining, String current, List result) {
// 非空才添加(跳过初始空串)
if (!current.isEmpty()) {
result.add(current);
}
// 对 remaining 中每个字符,作为下一个字符,其余字符继续递归
for (int i = 0; i < remaining.length(); i++) {
char c = remaining.charAt(i);
String nextRemaining = remaining.substring(0, i) + remaining.substring(i + 1);
solve(nextRemaining, current + c, result);
}
}
} 输出结果:
A,B,C AB,AC,BA,BC,CA,CB ABC,ACB,BAC,BCA,CAB,CBA
✅ 完全匹配题目期望格式(按长度分行,逗号分隔,无重复,含所有排列)。
⚠️ 注意事项:
- 该算法时间复杂度为 O(∑ₖ₌₁ⁿ P(n,k)) = O(⌊e·n!⌋),对长字符串(如 n > 10)慎用;
- 若输入含重复字符(如 "AAB"),需先排序 + 剪枝去重(在循环内跳过相同相邻字符);
- HashSet 不适用于本题,因题目要求显式列出所有序列而非仅计数,且顺序敏感,不可用集合自动去重("AB" 和 "BA" 必须同时存在)。
总结:生成“非空有序子序列”的本质是对所有非空长度执行无放回全排列。关键在递归时动态缩短可用字符池(substring 构造剩余字符串),而非固定索引推进——这确保了每个位置都能自由选择任意未用字符,从而覆盖全部排列空间。










