当前位置: 首页 > news >正文

字典序最小的拼接字符串(贪心+全排列)详解

字典序最小的拼接字符串(贪心+全排列)详解

📌 题目描述

给定一个长度为 $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

📤 输出描述

输出一个字符串,表示所有可能拼接方式删除一个字符后的 字典序最小字符串


🧠 解题思路详解

题目核心要求是:

  • 枚举所有 排列(字符串顺序)
  • 每种排列拼接后,再枚举所有 删除一个字符的可能性
  • 从所有结果中找出 字典序最小 的那个

但我们可以使用 贪心策略 来优化删除字符的过程,从而避免双重暴力。

🧮 详细算法流程

  1. 全排列枚举字符串顺序

    • 对数组下标使用 next_permutation 枚举所有排列,共有 $n!$ 种。
  2. 拼接字符串

    • 按当前排列顺序将所有字符串拼接成一个长串 $s$。
  3. 贪心删除一个字符

    • 在字符串 $s$ 中,找到第一个位置 $i$ 满足:

      s[i]>s[i+1] s[i] > s[i+1] s[i]>s[i+1]

    • 删除位置 $i$ 上的字符即可,能获得当前字典序最小的结果。

    • 如果不存在这样的 $i$,说明字符串已经是递增的,删除末尾字符。

  4. 更新最小字典序

    • 每轮操作后记录当前结果,如果比之前结果更小则更新。

⏱️ 复杂度分析

  • 排列枚举:$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

💡 总结

  • 本题看似是全排列 + 暴力删除,实则通过贪心策略降低删除复杂度。
  • 学会使用 下降点删除法 这种技巧,在字典序优化问题中非常常见。
  • 全排列搭配贪心,是解决小规模组合优化问题的典范方案。
http://www.dtcms.com/a/313235.html

相关文章:

  • 数据分析—numpy库
  • Pytorch-03数据的Transform
  • 2106. 摘水果,梳理思路
  • 新手向:Python制作贪吃蛇游戏(Pygame)
  • Redis面试精讲 Day 9:Redis模块开发与扩展
  • 信创数据库-DM(达梦)数据库安装教程
  • Rust:如何访问 *.ini 配置文件?
  • 【项目日志|苍穹外卖】 Day1:项目环境搭建与架构设计
  • 反向代理+网关部署架构
  • Java学习第一百零二部分——API网关
  • Claude Code入门学习笔记(五)--Claude Code命令行输入
  • 大白话讲解MCP
  • 多种单文件版分析型数据库调用底层函数对比
  • [Oracle] TO_DATE()函数
  • cs285 lecture13
  • 手机端使用表格填写表单问题
  • 复现YOLOV5+训练指定数据集
  • STM32-ESP8266通过MQTT与阿里云通讯
  • MySQL连接算法和小表驱动大表的原理
  • 李宏毅深度学习教程 第8-9章 生成模型+扩散模型
  • 【Django】-7- 实现注册功能
  • 09.Redis 常用命令
  • Android 之 蓝牙通信(2.0 经典)
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-58,(知识点:硬件产品的功率优化)
  • C++中多线程和互斥锁的基本使用
  • 【RH124 问答题】第 8 章 监控和管理 Linux 进程
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现实时食物水果的检测识别(C#代码UI界面版)
  • 使用 Spring Initializr 生成项目结构:Java 开发效率提升指南
  • 【QT】常⽤控件详解(二)windowOpacitycursorfontsetToolTipfocusPolicystyleSheet
  • 大语言模型涉及的一些概念(持续更新)