目录
问题描述
你要编写一个程序,该程序必须从一组给定的字母中生成所有可能的单词。
示例:给定单词“abc”,您的程序应该通过探索三个字母的所有不同组合来输出单词“abc”、“acb”、“bac”、“bca”、“cab”和“cba”。
在从输入文件中获取的单词中,某些字母可能会多次出现。对于给定的单词,程序不应多次生成相同的单词,并且单词应按字母升序输出。
输入
输入由几个单词组成。第一行包含一个数字,给出要遵循的单词数。以下每行包含一个单词。单词由从 A 到 Z 的大写或小写字母组成,大写和小写字母被视为不同。每个单词的长度小于 13。
输出
对于输入中的每个单词,输出应包含可以使用给定单词的字母生成的所有不同单词。从同一输入词生成的单词应按字母升序输出。大写字母位于相应的小写字母之前。
解题思路
考虑到输入的序列可能无序,首先给输入序列排序。通过给每个可能的字母一个唯一的序列号实现。当然,也可以写一个cmp函数用sort函数排序,更简单。这不是重点,咱们还是把目光主要放在permutation里吧。
然后用递归实现排列。第i层给出第i个字母的可能取值。为了防止重复输出,需要在每层循环时加一个判断条件:如果这个字母在之前已经排列过,就跳过它。为了保证输出的有序性,不用回溯,回溯会破坏有序性。比如:第一个字母排完后,让他和第二个字母交换位置,这样第三个字母交换时就会跟原来的第二个字母进行交换。但是需要注意:每i层递归退出时应该保证退出时的序列顺序与进入时的序列相同,否则将导致上一层继续递归的序列与一开始给的序列不符。比如:排序abc:abc,acb当在运行下一步时,应该排序bac,但会排成cab。递归主要关注的就是两个地方:一个是当前层,一个是当前层与更小层的关联。交换会导致当前层能遍历到所有的给定的字母,且不影响下一层遍历所有字母,每一层返回时应把序列返回到进入时的状态,不然就会影响上一层的遍历。因为在c++中string是值传入,所以这一步可以不用反应到代码当中。但如果用数组,就必须在代码当中体现。因为数组是引用传递。permutaton的m是当前层,n是最多能到达的层数,即到第n层所有字母都被选取了,就该输出啦。
AC代码
我写的最简洁的代码(用sort进行比较):
#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
void swap(string &s, int a, int b)
{
char t = s[a];
s[a] = s[b];
s[b] = t;
}
bool cmp(char a, char b)
{
double aa = a;
double bb = b;
double d = 'a' - 'A' - 0.5;
if (aa >= 'a')
{
aa = a - d;
}
if (bb >= 'a')
{
bb = b - d;
}
return aa < bb;
}
void permutation(string s, int m, int n)
{
if (m == n)
{
cout << s;
cout << endl;
}
else
{
for (int i = m; i <= n; i++)
{
if (i != m && s[i] == s[m])
{
continue;
}
swap(s, i, m);
permutation(s, m + 1, n);
}
}
}
int main(int argc, char const *argv[])
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
string str;
cin >> str;
sort(str.begin(), str.end(), cmp);
permutation(str, 0, str.size() - 1);
}
return 0;
}
不用sort函数自己排序的代码:
#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
struct word
{
int order;
char name;
};
void swap(string &s, int a, int b)
{
char t = s[a];
s[a] = s[b];
s[b] = t;
}
void permutation(string s, int m, int n)
{
if (m == n)
{
cout << s;
cout << endl;
}
else
{
for (int i = m; i <= n; i++)
{
if (i != m && s[i] == s[m])
{
continue;
}
swap(s, i, m);
permutation(s, m + 1, n);
}
}
}
int main(int argc, char const *argv[])
{
// freopen("../luogu/text.in", "r", stdin);
word w[26 * 2];
for (int i = 0, j = -1; i < 26 * 2; i++)
{
w[i].order = i;
if (i % 2 == 0)
{
j++;
w[i].name = 'A' + j;
}
else
w[i].name = 'a' + j;
}
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
string str;
cin >> str;
vector<word> b; // 待排列的数组
for (int j = 0; j < str.size(); j++)
{
int a = 0;
// 找到该字母的order
for (int k = 0; k < 26 * 2; k++)
{
if (w[k].name == str[j])
{
a = w[k].order;
break;
}
}
word c; // 待插入的数
c.name = str[j];
c.order = a;
if (b.size() == 0)
{
b.push_back(c);
}
else
{
int p = 0;
while (p < b.size() && b[p].order < a)
{
p++;
}
if (p == b.size())
{
b.push_back(c);
}
else
b.insert(b.begin() + p, c);
}
}
string s;
for (int j = 0; j < b.size(); j++)
{
s.push_back(b[j].name);
}
permutation(s, 0, s.size() - 1);
}
return 0;
}
用字符数组AC的代码
通过学习别人的博客,我发现如果传进去数组,应该这样写permutation函数
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
bool cmp(char a, char b)
{
double aa = a;
double bb = b;
double d = 'a' - 'A' - 0.5;
if (aa >= 'a')
{
aa = a - d;
}
if (bb >= 'a')
{
bb = b - d;
}
return aa < bb;
}
void swap(char *s, int a, int b)
{
int t = s[a];
s[a] = s[b];
s[b] = t;
}
void mycopy(char *a, char *b, int size)
{
for (int j = 0; j < size; j++)
{
a[j] = b[j];
}
}
void permutation(char *s, int m, int n)
{
if (m == n)
{
printf("%s\n", s);
}
else
{
char t[100];
for (int i = m; i < n + 1; i++)
{
if (i == m)
{
mycopy(t, s, n + 1);
}
// 注意此处需要特判一个特殊情况,即下面的if语句。因为第二个if语句的continue可能导致之后的代码不执行而出现问题(无法回溯)
if (i == n && i != m && s[i] == s[m])
{
mycopy(s, t, n + 1);
continue;
}
if (i != m && s[i] == s[m])
{
continue;
}
swap(s, i, m);
permutation(s, m + 1, n);
if (i == n)
{
mycopy(s, t, n + 1);
}
}
}
}
int main(int argc, char const *argv[])
{
int n;
cin >> n;
while (n--)
{
char s[100];
cin >> s;
int lenth = strlen(s);
sort(s, s + lenth, cmp);
permutation(s, 0, lenth - 1);
}
return 0;
}