您的位置:首页 > 职场人生

面试28字符串的排列组合

2015-07-13 12:52 429 查看
字符串的全排列:分两个步骤:1)第一个步 求 把第一个字符依次和后面的字符交换 即求所有可能出现在第一个位置的的字符,即 2)第二部固定第一个字符求后面所有字符的全排列

以abc 为例

abc,对应的全排列有6种:abc、acb、bca、bac、cab和cba。它可以由下述规则得到:

abc的全排列可以由下述规律得到:

a + 全排列(bc)

b + 全排列(ac)

c + 全排列(ba)

而各个子串全排列可以看作总串的一个相同但规模较小的问题,即用递归解决

文字描述:以三个字符abc为例来分析一下求字符串排列的过程。首先我们固定第一个字符a,求后面两个字符bc的排列。当两个字符bc的排列求好之后,

我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列。现在是把c放到第一位置的时候了。

记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍然是和原先处在第一位置的a交换,我们在拿c和

第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处在第一位置的a进行交换,得到cba。我们再次固定第一个字符c,

求后面两个字符b、a的排列

1、全排列就是从第一个数字起每个数分别与它后面的数字交换。

2、去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<vector>
#include<assert.h>
using namespace std;
//对字符串进行全排列 这个全排列不能够去掉重复的。若不考虑字符串中有重复字符(即假设字符串中无重复字符)
/*
以三个字符abc为例来分析一下求字符串排列的过程。首先我们固定第一个字符a,求后面两个字符bc的排列。当两个字符bc的排列求好之后,
我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列。现在是把c放到第一位置的时候了。
记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍然是和原先处在第一位置的a交换,我们在拿c和
第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处在第一位置的a进行交换,得到cba。我们再次固定第一个字符c,
求后面两个字符b、a的排列*/
template<class T> void Swap(T& a,T&b)
{
T temp=a;
a=b;
b=temp;
}
void Permutation(char *pstr,char *pbegin)//pBegin 为当前做排列操作的字符串的第一个字符
{
if(*pbegin=='\0')//当全排列到最后一个字符时终止
{
printf("%s\n",pstr);
}
else
{
for(char *pch=pbegin;*pch!='\0';pch++)//固定第一个字符的位置 把第一个字符和后面的所有字符交换
{
Swap(*pbegin,*pch);
Permutation(pstr,pbegin+1);
Swap(*pbegin,*pch);
}
}
}
void Permutation(char *pstr)
{
if(pstr==NULL)
{
return ;
}
Permutation(pstr,pstr);
}
//若考虑字符串中有重复字符(即假设字符串中有重复字符)
//方法是判断当前*pCh的字符(即准备与*pBegin交换的字符)在前面的子字符串中是否出现过了,若出现了,就不交换,若没出现就交换
void Permutation2(char* pSrc,  char* pBegin)  //pBegin 为当前做排列操作的字符串的第一个字符
{
if (!pSrc || !pBegin)
{
return;
}

if (*pBegin == '\0')
{
printf("%s\n", pSrc);
}
else
{
for (char* pCh=pBegin; *pCh!='\0'; ++pCh)  //固定第一个字符的位置 把第一个字符和后面的所有字符交换
{
if(strchr(pBegin, *pCh) == pCh)//strchr函数原型:extern char *strchr(const char *s,char c);查找字符串s中首次出现字符c的位置
{
Swap(*pBegin,*pCh);
Permutation(pSrc, pBegin+1);
Swap(*pBegin,*pCh);
}
}
}
}

//对整形数组 int a[]={12,23,34} 进行全排列
void PermutationInt(int *a,int length,int indexBegin);
void PermutationInt(int *a,int length)
{
if(a==NULL||length<=0)
{
return;
}
int indexBegin=0;
PermutationInt(a,length,indexBegin);
}
void PermutationInt(int *a,int length,int indexBegin)//indexBegin为当前做排列操作的第一个数字
{
if(indexBegin==length)//当全排列到最后一个数字字时终止
{
for(int i=0;i<length;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
else
{
for(int index=indexBegin;index<length;index++)
{
Swap(a[indexBegin],a[index]);
PermutationInt(a,length,indexBegin+1);
Swap(a[indexBegin],a[index]);
}
}
}
//字符串的组合  字符串的组合需要辅助空间
void Combination(char *str ,int m,vector<char> &result);

void Combination(char *str)
{
assert(str != NULL);
vector<char> result;
int i , length = strlen(str);
for(i = 1 ; i <= length ; ++i)//长度为length的字符串,能构成长度为1的组合,长度为2的组合.....长度为length的组合
{
Combination(str , i ,result);//在长度为length的字符串中  求字符串长度为i的组合
}
}

void Combination(char *str,int m , vector<char> &result)//求字符串str中长度为m的组合 组合存在vector<char>中
{
assert(str!= NULL);
if(m == 0)//组合到长度为0的组合为止
{
static int num = 1;
printf("第%d个组合\t",num++);

vector<char>::iterator iter = result.begin();
for( ; iter != result.end() ; ++iter)
{
printf("%c",*iter);
}
printf("\n");
return ;
}
if(*str == '\0')
return ;
result.push_back(*str);//(第一个字符包含在组合中)
Combination(str + 1 , m - 1 , result);//求n-1个字符串中长度为m-1的组合 (第一个字符包含在组合中)
result.pop_back();//(第一个字符不包含在组合中)
Combination(str+ 1 , m, result);//求n-1个字符串中长度为m的组合(第一个字符不包含在组合中)
}
//8皇后问题其实就是对整形数组ColumnIndex[8]={0,1,2,3,4,5,6,7} 中的8个元素进行全排列然后去掉在同一对角线上的全排列    即可
int g_number=0;
void Permutation3(int ColumnIndex[] , int length , int index);//其实就是对整形数组的全排列
void Print(int ColumnIndex[] , int length);
void EightQueen( )
{
const int queens = 8;
int ColumnIndex[queens];
for(int i = 0 ; i < queens ; ++i)
ColumnIndex[i] = i;    // 初始化
Permutation3(ColumnIndex , queens , 0);//索引为0表示从第一个数字开始进行全排列
}

bool Check(int ColumnIndex[] , int length)//检查任意两个不同的皇后是否在同一对角线上
{
int i,j;
for(i = 0 ; i < length; ++i)
{
for(j = i + 1 ; j < length; ++j)//判读两个不同的皇后。i!=j
{
if( i - j == ColumnIndex[i] - ColumnIndex[j] || j - i == ColumnIndex[i] - ColumnIndex[j])   //在正、副对角线上
return false;
}
}
return true;
}
//其实就是对整形数组ColumnIndex[8]={0,1,2,3,4,5,6,7} 中的8个元素进行全排列
void Permutation3(int ColumnIndex[] , int length , int index)
{
if(index == length)//当全排列到最后一个数组元素时停止全排列
{
if( Check(ColumnIndex , length) )   //检测棋盘当前的状态是否合法
{
++g_number;//记录有多少中方法
Print(ColumnIndex , length);
}
}
else
{
for(int i = index ; i < length; ++i)   //全排列 将第一个数字依次和后面的所有数字交换,使每个数字都有在第一个位置出现的可能。
{
swap(ColumnIndex[index] , ColumnIndex[i]);
Permutation3(ColumnIndex , length , index + 1);//固定第一个数字,后面的所有数字进行全排列
swap(ColumnIndex[index] , ColumnIndex[i]);
}
}
}

void Print(int ColumnIndex[] , int length)
{
printf("8皇后的第%d中放法:\n",g_number);
for(int i = 0 ; i < length; ++i)
printf("%d ",ColumnIndex[i]);//表示第i 行的8皇后放在第ColumnIndex[i] 列
printf("\n");
}

int main()
{
char ch[100]="aaa";
//cin>>ch;
Permutation(ch);
cout<<"考虑字符串中有重复字符的全排列"<<endl;
Permutation2(ch,ch);
cout<<"整形数组的全排列"<<endl;
int a[]={12,23,34};
cout<<endl;
PermutationInt(a,3);
cout<<"字符串的组合"<<endl;
Combination(ch);
cout<<"8皇后问题"<<endl;
EightQueen();
return 0;
}


二、字符串的组合

(1)若不考虑字符串中有重复字符(即假设字符串中无重复字符)

输入一个字符串,输出该字符串中字符的所有组合。举个例子,如果输入"abc",它的组合有长度为1的组合:a、b、c 长度为2的组合:ab、ac、bc长度为3的组合:abc

本题也可以用递归的思路来求字符串的组合。 假设我们想在长度为n的字符串中求m个字符的组合。将n个字符分为两部分,第一个字符和其余n-1个字符
扫描第一个字符时我们有两种选择:一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;二是
不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符。这两种选择都很容易用递归实现

长度为length的字符串,能构成长度为1的组合,长度为2的组合.....长度为length的组合
求n个字符组成长度为m的组合问题,可以分解成两个子问题,1)求n-1个字符串中长度为m-1的组合 (第一个字符包含在组合中) 2)求n-1个字符串中长度为m的组合(第一个字符不包含在组合中)

void Combination(char *str,int m , vector<char> &result)//求字符串str中长度为m的组合 组合存在vector<char>中
{
assert(str!= NULL);
if(m == 0)//组合到长度为0的组合为止
{
static int num = 1;
printf("第%d个组合\t",num++);

vector<char>::iterator iter = result.begin();
for( ; iter != result.end() ; ++iter)
{
printf("%c",*iter);
}
printf("\n");
return ;
}
if(*str == '\0')
return ;
result.push_back(*str);//(第一个字符包含在组合中)
Combination(str + 1 , m - 1 , result);//求n-1个字符串中长度为m-1的组合 (第一个字符包含在组合中)
result.pop_back();//(第一个字符不包含在组合中)
Combination(str+ 1 , m, result);//求n-1个字符串中长度为m的组合(第一个字符不包含在组合中)
}


字符串全排列扩展----八皇后问题

8皇后问题其实就是对整形数组ColumnIndex[8]={0,1,2,3,4,5,6,7} 中的8个元素进行全排列然后去掉在同一对角线上的全排列即可 ColumnIndex[i] 下标索引号i为皇后所在行号,ColumnIndex[i]表示第i行皇后所在列号,即在第ColumnIndex[i]列

题目:在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行、同一列或者同一对角斜线上。下图中的每个黑色格子表示一个皇后,这就是一种符合条件的摆放方法。请求出总共有多少种摆法。



这就是有名的八皇后问题。解决这个问题通常需要用递归,而递归对编程能力的要求比较高。因此有不少面试官青睐这个题目,用来考察应聘者的分析复杂问题的能力以及编程的能力。

由于八个皇后的任意两个不能处在同一行,那么这肯定是每一个皇后占据一行。于是我们可以定义一个数组ColumnIndex[8],数组中第i个数字表示位于第i行的皇后的列号。先把ColumnIndex[8]的八个数字分别用0-7初始化,接下来我们要做的事情就是对数组ColumnIndex[8]做全排列。由于我们是用不同的数字初始化数组中的数字,因此任意两个皇后肯定不同列。我们只需要判断得到的每一个排列对应的八个皇后是不是在同一对角斜线上,也就是数组的两个下标i和j,是不是i-j==ColumnIndex[i]-ColumnIndex[j]或者j-i==ColumnIndex[i]-ColumnIndex[j]。

对于任意两个不在同一行上的皇后,i!=j; 其中i表示在第i 行,第ColumnIndex[i]表示第 ColumnIndex[i]列的皇后行列组为(i,ColumnIndex[i])(j,ConlumnIndex[j])
判断第i行上的皇后:(i,ColumnIndex[i])和第j
行上的皇后:(j,ConlumnIndex[j])是否在主对角线上只要判断 i-j==ColumnIndex[i]-ColumnIndex[j]

判断第i行上的皇后:(i,ColumnIndex[i])和第j
行上的皇后:(j,ConlumnIndex[j])是否在副对角线上只要判断 j-i==ColumnIndex[i]-ColumnIndex[j]


关于排列的详细讨论,详见上面的讲解。

接下来就是写代码了。思路想清楚之后,编码并不是很难的事情。下面是一段参考代码:
//8皇后问题可以用全排列来做
int g_number=0;
void Permutation3(int ColumnIndex[] , int length , int index);//其实就是对整形数组的全排列
void Print(int ColumnIndex[] , int length);
void EightQueen( )
{
const int queens = 8;
int ColumnIndex[queens];
for(int i = 0 ; i < queens ; ++i)
ColumnIndex[i] = i;    //初始化
Permutation3(ColumnIndex , queens , 0);
}

bool Check(int ColumnIndex[] , int length)
{
int i,j;
for(i = 0 ; i < length; ++i)
{
for(j = i + 1 ; j < length; ++j)
{
if( i - j == ColumnIndex[i] - ColumnIndex[j] || j - i == ColumnIndex[i] - ColumnIndex[j])   //在正、副对角线上
return false;
}
}
return true;
}
//其实就是对整形数组ColumnIndex[8]={0,1,2,3,4,5,6,7} 中的8个元素进行全排列
void Permutation3(int ColumnIndex[] , int length , int indexBegin)//indexBegin 表示当前正在做全排操作的第一个数字的索引
{
if(indexBegin == length)
{
if( Check(ColumnIndex , length) )   //检测棋盘当前的状态是否合法
{
++g_number;//记录有多少中方法
Print(ColumnIndex , length);
}
}
else
{
for(int i = indexBegin ; i < length; ++i)   //全排列
{
swap(ColumnIndex[indexBegin] , ColumnIndex[i]);
Permutation3(ColumnIndex , length , indexBegin + 1);
swap(ColumnIndex[indexBegin] , ColumnIndex[i]);
}
}
}

void Print(int ColumnIndex[] , int length)
{
printf("8皇后的第%d中放法:\n",g_number);
for(int i = 0 ; i < length; ++i)
printf("%d ",ColumnIndex[i]);//表示第i 行的8皇后放在第ColumnIndex[i] 列
printf("\n");
}


题目:输入两个整数n和m,从数列1,2,3...n中随意取几个数,使其和等于m,要求列出所有的组合。

[cpp] view
plaincopy

#include <iostream>

#include <list>

using namespace std;

list<int> list1;

void find_factor(int sum,int n)

{

//递归出口

if(n<=0||sum<=0)

return;

//输出找到的数

if(sum==n)

{

list1.reverse();

for(list<int>::iterator iter=list1.begin();iter!=list1.end();iter++)

cout<<*iter<<"+";

cout<<n<<endl;

list1.reverse();

}

list1.push_front(n);

find_factor(sum-n,n-1);//n放在里面

list1.pop_front();

find_factor(sum,n-1);//n不放在里面

}

int main(void)

{

int sum,n;

cin>>sum>>n;

cout<<"所有可能的序列,如下:"<<endl;

find_factor(sum,n);

return 0;

}

转自:http://blog.csdn.net/hackbuteer1/article/details/7462447
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: