题目描述
给定一个全由小写字母构成的字符串,求它的全排列,按照字典序从小到大输出。
输入格式:
一行,一个字符串,长度不大于8。
输出格式:
输出所有全排列,每行一种排列形式,字典序从小到大。
输入样例:
在这里给出一组输入。例如:
abc
输出样例:
在这里给出相应的输出。例如:
abc
acb
bac
bca
cab
cba
分治法解决步骤
- 分解问题:将字符串分解为一个字符和剩下的部分。对于每个字符,将它放在当前排列的开头位置,然后对剩下的字符进行递归全排列。
- 递归求解:在每次递归中,将剩下的字符分解并排列,直到字符串长度为1。
- 合并结果:在递归层级返回时,汇总每一层的结果,从而得到所有排列组合。
- 去重和排序:为确保字典序输出,在进入递归之前将字符排序,并跳过重复字符。
代码
- 使用set存储结果
#include <iostream>
#include <string>
#include <algorithm>
#include <set>
using namespace std;
void permute(string& str, int start, set<string>& result){
// 递归结束条件
if (start == str.size()){
// 处理完所有字符
result.insert(str);
return;
}
for(int i = start; i < str.size(); ++i){
swap(str[start], str[i]); // 交换当前i位置要固定的字符,使该位置的所有可能字符取到
permute(str, start + 1, result); // 固定当前字符,递归处理后面部分
swap(str[start], str[i]); // 恢复位置
}
}
int main() {
string input;
cin >> input;
// 使用set存储,可以避免对重复元素的处理
set<string> result;
permute(input, 0, result);
for(const auto &elem: result){
cout << elem << endl;
}
return 0;
}
- 使用vector存储结果
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
void permute(string &s, int start, vector<string> &result) {
// 基本情况:如果起始位置到字符串结尾,表示找到一个排列
if (start == s.size()) {
result.push_back(s);
return;
}
// 使用集合来跳过重复字符
for (int i = start; i < s.size(); ++i) {
// 跳过重复字符
if (i != start && s[i] == s[start]) continue;
swap(s[start], s[i]); // 交换当前i位置要固定的字符,使该位置的所有可能字符取到
permute(s, start + 1, result); // 递归求解后续字符的排列
swap(s[start], s[i]); // 还原字符位置
}
}
int main() {
string s;
cin >> s;
vector<string> result;
sort(s.begin(), s.end()); // 确保字符串按字典序排列
permute(s, 0, result);
for (const auto &elem : result) {
cout << elem<< endl;
}
return 0;
}
使用两种容器的性能分析
使用 std::set
来保存元素会有一些额外的性能开销,相比于 std::vector
或其他顺序容器,主要体现在以下几个方面:
1. 存储结构
std::set
:底层通常实现为红黑树(或其他平衡树),每次插入、删除和查找操作的时间复杂度为 (O(\log n))。插入时,元素会自动按照顺序排列。std::vector
:底层实现为动态数组,插入、删除操作的时间复杂度为 (O(n))(在最坏情况下),但查找操作在平均情况下是 (O(1)),适合随机访问。
2. 插入和查找的性能
- 当你插入元素到
std::set
时,容器会根据元素的值保持排序状态。因此,每次插入的开销相对较高,特别是对于大量元素。 - 而在
std::vector
中,插入元素的开销较低,尤其是在末尾插入的情况下,因为只需简单地增加大小和复制元素。但在插入时要维护顺序(例如排序)时,就需要额外的开销。
3. 内存使用
std::set
的内存开销通常比std::vector
大,因为它需要存储额外的指针来维护树的结构。std::vector
的内存使用更加紧凑,适合存储大量相同类型的元素。
4. 访问性能
std::set
只能通过迭代器访问元素,不能像std::vector
一样通过索引随机访问。这意味着访问特定元素时会更加耗时。
总结
如果你的主要需求是存储元素并自动保持它们的有序状态,且你不在意额外的性能开销,那么使用 std::set
是一个不错的选择。但如果你的应用需要频繁地进行随机访问或对大量元素进行插入/删除操作,使用 std::vector
可能会更有效率。
在很多情况下,如果不需要保持元素的唯一性和自动排序,而只是需要一个动态数组的特性,std::vector
通常会是更好的选择。你可以在使用 std::vector
后对其进行排序,虽然排序的时间复杂度是 (O(n \log n)),但在某些情况下这仍然会比使用 std::set
更高效。