字符串按字母序排列的最短操作路径及序列输出技术问询
解决字符串排序最短操作序列问题
嘿,我明白你的问题了:你需要用仅有的两种操作——b(交换前两个字符)和s(整体右移一位)——找到把任意字符串排成字母顺序的最短操作序列,但现在你的StringBuilder会把所有尝试过的操作都追加进去,没法精准输出那条最短的路径。
问题本质拆解
其实这个问题就是典型的状态空间最短路径搜索:每个字符串都是一个状态,两种操作就是状态之间的“移动方式”,我们要从初始状态(输入字符串)走到目标状态(排序后的字符串),找步数最少的那条路。
你之前的实现之所以会记录所有操作,大概率是用了深度优先搜索(DFS)或者没做正确的路径回溯/分支管理——DFS会一条路走到黑,尝试所有可能的分支,自然会把所有操作都存下来。而**广度优先搜索(BFS)**才是解决这类最短路径问题的最优选择,因为它是按“步数”逐层遍历的,第一次碰到目标状态时,对应的路径就是最短的。
具体实现思路
我给你梳理下清晰的解决步骤:
- 先确定目标状态:把输入字符串排序,得到我们最终要到达的那个有序字符串。
- 用BFS队列管理状态:队列里的每个元素要存两个东西——当前的字符串状态,以及走到这个状态用的操作序列。
- 用集合记录已访问状态:避免重复处理同一个字符串(不然会陷入循环,比如连续5次
s又回到原字符串)。 - 生成新状态并推进搜索:对队列里的每个状态,分别执行
b和s操作,生成新的字符串:- 执行
b:交换前两个字符,比如BECAD变EBCAD,操作序列加个b。 - 执行
s:把最后一个字符移到最前面,比如BECAD变DBECA,操作序列加个s。
- 执行
- 终止条件:一旦生成的新字符串等于目标状态,直接返回对应的操作序列——这就是最短路径。
代码示例(Java)
import java.util.*; public class ShortestSortOps { public static String getShortestSequence(String input) { // 先搞定目标状态:排序后的字符串 char[] targetArr = input.toCharArray(); Arrays.sort(targetArr); String target = new String(targetArr); // BFS队列:每个元素存当前字符串和对应的操作序列 Queue<Map.Entry<String, String>> queue = new LinkedList<>(); queue.add(new AbstractMap.SimpleEntry<>(input, "")); // 记录已经处理过的字符串,防止循环 Set<String> visited = new HashSet<>(); visited.add(input); while (!queue.isEmpty()) { Map.Entry<String, String> current = queue.poll(); String currentStr = current.getKey(); String ops = current.getValue(); // 找到目标了,直接返回操作序列 if (currentStr.equals(target)) { return ops; } // 执行b操作:交换前两个字符 if (currentStr.length() >= 2) { char[] bArr = currentStr.toCharArray(); char temp = bArr[0]; bArr[0] = bArr[1]; bArr[1] = temp; String bNewStr = new String(bArr); if (!visited.contains(bNewStr)) { visited.add(bNewStr); queue.add(new AbstractMap.SimpleEntry<>(bNewStr, ops + "b")); } } // 执行s操作:整体右移一位(等价于最后一个字符移到开头) if (currentStr.length() > 0) { char lastChar = currentStr.charAt(currentStr.length() - 1); String sNewStr = lastChar + currentStr.substring(0, currentStr.length() - 1); if (!visited.contains(sNewStr)) { visited.add(sNewStr); queue.add(new AbstractMap.SimpleEntry<>(sNewStr, ops + "s")); } } } // 兜底:理论上输入都能通过操作排序,这里只是防止极端情况 return ""; } public static void main(String[] args) { String testInput = "BECAD"; String result = getShortestSequence(testInput); System.out.println("最短操作序列:" + result); // 输出bsssbsb,步数7,和你预期的一致 } }
关键细节提醒
- 为什么BFS能找最短路径:BFS是按步数分层遍历的,第一层是1步操作能到的所有状态,第二层是2步的,以此类推。所以第一次遇到目标状态时,对应的步数肯定是最少的,不用再往下搜了。
- 访问集合的必要性:如果不记录已访问的字符串,队列会不断生成重复的状态,比如你连续执行5次
s又回到原字符串,程序会无限循环下去。 - 边界情况处理:当字符串长度小于2时,
b操作没意义,直接跳过就行;空字符串的情况你大概率不会遇到,但代码里也做了处理。
用这个思路改完之后,就不会再有StringBuilder乱追加操作的问题了——BFS只记录到达每个状态的最短路径,一旦找到目标就立刻返回,不会去遍历那些多余的分支。
内容的提问来源于stack exchange,提问作者xxFlashxx
相关产品推荐
相关产品推荐

