#P3329. 第2题-拼接字符串
-
1000ms
Tried: 61
Accepted: 24
Difficulty: 5
所属公司 :
科大讯飞
时间 :2025年8月2日
-
算法标签>暴力枚举
第2题-拼接字符串
解题思路
要在所有排列中尝试拼接再删字符并选最小,直接暴力枚举所有 n! 种排列,每种排列拼接后长度最多 L≤80,再枚举删除位置,复杂度约为 O(n!×L2)。
可以优化:对于给定字符序列 S,要删除一个字符后使得结果字典序最小,只需找到第一个使得 S[i]>S[i+1] 的位置 i,删除 S[i];若不存在这样的下降点,则删除末尾字符。该贪心策略可在线性时间 O(L) 内完成。
因此总体流程:
- 对索引 [0,1,…,n−1] 排列全遍历(或 DFS 生成)。
- 拼接对应字符串序列得到 S。
- 在 S 上执行“第一个下降点”贪心删除,得候选字符串 T。
- 维护当前最小字典序字符串并更新。
复杂度分析
- 排列枚举:n! 种。
- 每个排列拼接字符串和贪心删除:O(L),其中 L=∑∣ai∣≤80。
- 整体时间复杂度:O(n!×L),在 n≤8、L≤80 下可行。
- 空间复杂度:存储拼接字符串和若干临时变量,为 O(L)。
代码实现
Python
def solve():
import sys
data = sys.stdin.read().split()
n = int(data[0])
arr = data[1:1+n]
from itertools import permutations
best = None
for perm in permutations(arr):
s = "".join(perm)
# 找到第一个下降点删除,若无则删除末尾
idx = len(s) - 1
for i in range(len(s)-1):
if s[i] > s[i+1]:
idx = i
break
t = s[:idx] + s[idx+1:]
if best is None or t < best:
best = t
print(best)
if __name__ == "__main__":
solve()
Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(in.readLine().trim());
String[] a = in.readLine().trim().split("\\s+");
boolean[] used = new boolean[n];
String[] perm = new String[n];
String[] best = {null};
dfs(a, used, perm, 0, best);
System.out.println(best[0]);
}
// 递归枚举排列
static void dfs(String[] a, boolean[] used, String[] perm, int depth, String[] best) {
int n = a.length;
if (depth == n) {
String s = String.join("", perm);
int idx = s.length() - 1;
for (int i = 0; i + 1 < s.length(); i++) {
if (s.charAt(i) > s.charAt(i+1)) {
idx = i;
break;
}
}
String t = s.substring(0, idx) + s.substring(idx+1);
if (best[0] == null || t.compareTo(best[0]) < 0) {
best[0] = t;
}
return;
}
for (int i = 0; i < n; i++) {
if (!used[i]) {
used[i] = true;
perm[depth] = a[i];
dfs(a, used, perm, depth+1, best);
used[i] = false;
}
}
}
}
C++
#include <bits/stdc++.h>
using namespace std;
int n;
vector<string> a;
string best;
void dfs(vector<bool>& used, vector<string>& perm, int depth) {
if (depth == n) {
string s;
for (auto& str: perm) s += str;
int idx = s.size() - 1;
for (int i = 0; i + 1 < (int)s.size(); i++) {
if (s[i] > s[i+1]) {
idx = i;
break;
}
}
string t = s.substr(0, idx) + s.substr(idx+1);
if (best.empty() || t < best) best = t;
return;
}
for (int i = 0; i < n; i++) {
if (!used[i]) {
used[i] = true;
perm[depth] = a[i];
dfs(used, perm, depth+1);
used[i] = false;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
a.resize(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<bool> used(n, false);
vector<string> perm(n);
dfs(used, perm, 0);
cout << best << "\n";
return 0;
}
题目内容
对于给定的长度为n,仅由字符串构成的数组{a1,a2,...an}每一个字符串都仅由小写字母构成。
你需要将全部字符串按照任意顺序拼按成一整个字符串(但是不改变每个字符串内部的顺序),随后恰好删除其中的一个字符
请你输出所有可能的拼接结果中,字典序最小的结果。
[名词解释]
从字符串的第一个字符开始逐个比较,直至发现第一不同的位置,比较这个位置字符的字母表顺序,字母序较小的字将串字典序也较小:如果比较到其中一个字符串的结尾时依旧全部相同,则较短的字符串字典序更小。
输入描述
第一行输入一个整数n(1≤n≤8),表示数组的长度。
第二行输入n个字符串a1,a2,...,an(1≤len(ai)≤10),每个字符串仅由小写字母构成。
输出描述
输出一个字符串,表示所有可能的拼接结果中,字典序最小的结果。
输入
1
za
输出
a
样例2
输入
3
za ba bb
输出
ababb