字典序最小的拼接字符串(贪心+全排列)详解
字典序最小的拼接字符串(贪心+全排列)详解
📌 题目描述
给定一个长度为 $n$ 的字符串数组,每个字符串仅由 小写字母 组成。你的任务是:
- 将所有字符串 以任意顺序拼接 成一个大字符串(每个字符串内部顺序不能改变)。
- 然后从这个拼接后的字符串中恰好删除一个字符。
- 输出所有可能的结果中,字典序最小的一个字符串。
📖 名词解释:字典序
判断两个字符串的字典序大小规则如下:
-
从左向右逐字符比较,直到遇到第一个不同的字符。
- 比如
"abc"
<"bcd"
,因为'a'
<'b'
- 比如
-
若其中一个是另一个的前缀,长度较短的字符串字典序更小。
- 比如
"abc"
<"abcd"
- 比如
📥 输入描述
第一行:一个整数 n,表示字符串数组长度。
第二行:n 个字符串,空格分隔。
- $1 \leq n \leq 6$
- 每个字符串仅包含小写字母,长度 $\leq 10$
示例1:
输入:
1
za
输出:
a
示例2:
输入:
3
za ba bb
输出:
ababb
📤 输出描述
输出一个字符串,表示所有可能拼接方式删除一个字符后的 字典序最小字符串。
🧠 解题思路详解
题目核心要求是:
- 枚举所有 排列(字符串顺序)
- 每种排列拼接后,再枚举所有 删除一个字符的可能性
- 从所有结果中找出 字典序最小 的那个
但我们可以使用 贪心策略 来优化删除字符的过程,从而避免双重暴力。
🧮 详细算法流程
-
全排列枚举字符串顺序:
- 对数组下标使用
next_permutation
枚举所有排列,共有 $n!$ 种。
- 对数组下标使用
-
拼接字符串:
- 按当前排列顺序将所有字符串拼接成一个长串 $s$。
-
贪心删除一个字符:
-
在字符串 $s$ 中,找到第一个位置 $i$ 满足:
s[i]>s[i+1] s[i] > s[i+1] s[i]>s[i+1]
-
删除位置 $i$ 上的字符即可,能获得当前字典序最小的结果。
-
如果不存在这样的 $i$,说明字符串已经是递增的,删除末尾字符。
-
-
更新最小字典序:
- 每轮操作后记录当前结果,如果比之前结果更小则更新。
⏱️ 复杂度分析
- 排列枚举:$O(n!)$,最多 $6! = 720$ 次($n \leq 6$,完全可行)
- 拼接字符串:每次最多 $60$ 个字符
- 贪心删除字符:$O(L)$,$L$ 为拼接后长度
- 总复杂度:$O(n! \cdot L)$,实际运行效率非常快
✅ C++完整代码(附详细注释)
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;int main() {ios::sync_with_stdio(false); // 加速 cin/coutcin.tie(nullptr); // 解绑 cin 和 coutint n;cin >> n; // 读入字符串个数vector<string> arr(n); // 存放 n 个字符串for (int i = 0; i < n; ++i) {cin >> arr[i]; // 输入每个字符串}string best; // 记录当前字典序最小结果vector<int> idx(n); // 用于生成排列的下标数组for (int i = 0; i < n; ++i) idx[i] = i;// 枚举所有字符串排列顺序do {string s; // 当前排列下拼接的字符串for (int i : idx) {s += arr[i]; // 将字符串拼接成一个长串}// 贪心找删除位置:第一个下降点size_t del = s.size() - 1; // 默认删除最后一个字符for (size_t i = 0; i + 1 < s.size(); ++i) {if (s[i] > s[i + 1]) { // 找到下降点del = i;break;}}// 构造删除一个字符后的新字符串string t = s.substr(0, del) + s.substr(del + 1);// 如果当前更优,则更新最小字典序结果if (best.empty() || t < best) {best = move(t); // 移动语义,避免拷贝提升效率}} while (next_permutation(idx.begin(), idx.end())); // 生成下一个排列cout << best << '\n'; // 输出结果return 0;
}
🧪 测试说明
你可以通过以下用例验证代码:
示例输入1:
1
za
输出:
a
示例输入2:
3
za ba bb
输出:
ababb
💡 总结
- 本题看似是全排列 + 暴力删除,实则通过贪心策略降低删除复杂度。
- 学会使用 下降点删除法 这种技巧,在字典序优化问题中非常常见。
- 全排列搭配贪心,是解决小规模组合优化问题的典范方案。