【题目链接】
ybt 1199:全排列
OpenJudge NOI 2.2 1750:全排列
注意:一本通网站该题有特判,不能引入<bits/stdc++.h>
,会被认为使用stl函数。
【题目考点】
1. 递归
2. 深搜
【解题思路】
解法1:递归
已知原字符串下标从0开始,长度为len。
- 递归问题:求原字符串的下标从k到len-1这一子串的全排列
- 递归关系:
分别以该子串中每个字符作为第一个字符,即下标i从k到len-1遍历,以每个位置的字符s[i]作为这个子串的第一个字符,也就是原字符串下标k的位置,将遍历到的字符s[i]与s[k]交换。由于原字符串是升序的,经过这种方式交换后,下标k到len-1的字符仍然是升序的。每次交换后,递归求原字符串下标k+1到len-1这一子串的全排列。 - 递归出口:
如果k等于len-1,那么这个子串长度为1,只有一种排列。此时输出整个字符串, 即为原字符串的一种全排列。
示例:求abc的全排列
- 先以a为第一个字符,求bc的全排列。
- 求bc的全排列:
- 以b为第一个字符,求c的全排列,为c。加上前面的b,为bc。
- 以c为第一个字符,求b的全排列,为b。加上前面的c,为cb。
- 前面加上字符a,得到全排列abc, acb。
- 以b为第一个字符,求ac的全排列,过程略,有ac, ca。加上第一个字符b,有全排列bac,bca。
- 以c为第一个字符,求ab的全排列,过程略,有ab, ba。加上第一个字符c,有全排列cab,cba。
注意:每次调用结束前,都要将经过求全排列后打乱的字符串还原为调用函数时的状态。
在递归调用后,k+1位置是最大的字符,k+2到len-1是升序字符串。只需要将k+1位置的字符通过不断地交换移动到len-1位置即可。(这个过程类似于冒泡排序中的冒泡过程。)
解法2:深搜
设字符数组a
保存要输出的排列结果。
设vis
数组,vis[i]
为真表示字符串下标i的字符已经使用过了。
每次递归调用确定第k位置的字符
遍历字符串,看哪个字符没有使用过,那么就选择该字符作为第k位置的字符,而后确定第k+1位置的字符。
如果已经确定完第len-1字符,那么此时输出数组a
。
注意状态还原。
【题解代码】
解法1:递归
#include<iostream>
#include<cstring>
using namespace std;
char s[10];
int len;//len为s的长度
//确定第k位置的字符,并保证下标k+1~len-1是升序字符串
void arrange(int k)
{
if(k == len-1)//如果看到最后一个字符
{
cout << s << endl;//此时s就是一种排列
return;
}
for(int i = k; i < len; ++i)
{
swap(s[k], s[i]);//交换i指向的元素和第k元素,交换后s[k+1]~s[len-1]仍然是升序字符串
arrange(k+1);//确定下一个字符
}
for(int i = k; i < len-1; ++i)//数组还原
swap(s[i], s[i+1]);
}
int main()
{
cin >> s;
len = strlen(s);
arrange(0);
return 0;
}
解法2:深搜
- 写法1:判断解写在循环内
#include<iostream>
#include<cstring>
using namespace std;
char s[10], a[10];
int len;//len为s的长度
bool vis[10];//vis[i]:第i字符是否用过
//确定第k个字符
void dfs(int k)
{
for(int i = 0; i < len; ++i)
{
if(vis[i] == false)//如果第i字符没用过
{
vis[i] = true;//选择第i字符
a[k] = s[i];//填充到第k位置
if(k == len-1)//如果找到解
cout << a << endl;//a是全局变量,末尾自然有'\0',此时一定填了len个元素,是一个字符数组
else
dfs(k+1);//看下一个字符
vis[i] = false;
}
}
}
int main()
{
cin >> s;
len = strlen(s);
dfs(0);
return 0;
}
- 写法2:判断解写在循环外
#include<iostream>
#include<cstring>
using namespace std;
char s[10], a[10];
int len;//len为s的长度
bool vis[10];//vis[i]:第i字符是否用过
//确定第k个字符
void dfs(int k)
{
if(k == len)
{
cout << a << endl;
return;
}
for(int i = 0; i < len; ++i)
{
if(vis[i] == false)//如果第i字符没用过
{
vis[i] = true;
a[k] = s[i];
dfs(k+1);
vis[i] = false;
}
}
}
int main()
{
cin >> s;
len = strlen(s);
dfs(0);
return 0;
}