字符串的排列組合問題--

問題1:輸入一個字符串,打印該字符串中字符的全部排列,例如輸入字符串abc,則輸出由字符abc所能排列出來的全部字符串abcacbbacbcacabcba 算法

思路:這是個遞歸求解的問題。遞歸算法的四個特性:(1)必須有可達到的終止條件,不然程序將陷入死循環;(2)子問題在規模上比原問題小;(3)子問題可經過再次遞歸調用求解;(4)子問題的解能組合成整個問題的解。 函數

對於字符串的排列問題。若是能生成n - 1個元素的全排列,就能生成n個元素的全排列。對於只有1個元素的集合,能夠直接生成全排列。全排列的遞歸終止條件很明確,只有1個元素時。下面這個圖很清楚的給出了遞歸的過程。 spa


參考代碼:解法1經過Permutation_Solution1(str,0,n)(Permutation置換;排列);解法2經過調用Permutation_Solution2(str,str)來求解問題。

//函數功能:求一個字符串某個區間內字符的全排列
//函數參數:pStr爲字符串,begin和end表示區間
//返回值:無
void Permutation_Solution1(char *pStr,int begin,int end)
{
    if(begin == end-1)//只剩一個元素
    {
      for(int i=0;i<end;i++)//打印
         cout<<pStr[i];
      cout<<endl;
    }
    else
    {
	   for(int k=begin;k<end;k++)
	   {
	       swap(pStr[k],pStr[begin]);//交換兩個字符
		   Permutation_Solution1(pStr,begin-1,end);
		   swap(pStr[k],pStr[begin]);//恢復
		}
	}
}
//函數功能:求一個字符串某個區間內字符的全排列
//函數參數:pStr爲字符串,pBegin爲開始位置
//返回值:無
void Permutation_Solution2(char *pStr,char *pBegin)
{
   if(*pBegin == '\0')
   {
      cout<<pStr<<endl;
	}
	else
	{
	   char *pCh = pBegin;
	   while(*pCh != '\0')
	   {
	      swap(*pBegin,*pCh);
		  Permutation_Solution2(pStr,pBegin+1);
		  swap(*pBegin,*pCh);
		  pCh++;
		}
	}
}
//提供的公共接口
void Permutation(char *pStr)
{
Permutation_Solution1(pStr,0,strlen(pStr));
//Permutation_Solution2(pStr,pStr));????
}
問題2:輸入一個字符串,輸出該字符串中字符的全部組合。舉個例子,若是輸入abc,它的組合有a、b、c、ab、ac、bc、abc。

思路:一樣是用遞歸求解。能夠考慮求長度爲n的字符串中m個字符的組合,設爲C(n,m)。原問題的解即爲C(n, 1), C(n, 2),...C(n, n)的總和。對於求C(n, m),從第一個字符開始掃描,每一個字符有兩種狀況,要麼被選中,要麼不被選中,若是被選中,遞歸求解C(n-1, m-1)。若是未被選中,遞歸求解C(n-1, m)。無論哪一種方式,n的值都會減小,遞歸的終止條件n=0或m=0。 code

//函數功能 : 從一個字符串中選m個元素
//函數參數 : pStr爲字符串, m爲選的元素個數, result爲選中的
//返回值 :   無
void Combination_m(char *pStr, int m, vector<char> &result)
{
	if(pStr == NULL || (*pStr == '\0'&& m != 0))
		return;
	if(m == 0) //遞歸終止條件
	{
		for(unsigned i = 0; i < result.size(); i++)
			cout<<result[i];
		cout<<endl;
		return;
	}
	//選擇這個元素
	result.push_back(*pStr);
	Combination_m(pStr + 1, m - 1, result);
	result.pop_back();
	//不選擇這個元素
	Combination_m(pStr + 1, m, result);
}
//函數功能 : 求一個字符串的組合
//函數參數 : pStr爲字符串
//返回值 :   無
void Combination(char *pStr)
{
	if(pStr == NULL || *pStr == '\0')
		return;
	int number = strlen(pStr);
	for(int i = 1; i <= number; i++)
	{
		vector<char> result;
		Combination_m(pStr, i, result);
	}
}
問題3:打靶問題。一個射擊運動員打靶,靶一共有10環,連開10 槍打中90環的可能性有多少?

 思路:這道題的思路與字符串的組合很像,用遞歸解決。一次射擊有11種可能,命中1環至10環,或脫靶。 遞歸

//函數功能 : 求解number次打中sum環的種數
//函數參數 : number爲打靶次數,sum爲須要命中的環數,result用來保存中間結果,total記錄種數 
//返回值 :   無
void ShootProblem_Solution1(int number, int sum, vector<int> &result, int *total)
{
	if(sum < 0 || number * 10 < sum) //加number * 10 < sum很是重要,它能夠減小大量的遞歸,相似剪枝操做
		return;
	if(number == 1) //最後一槍
	{
		if(sum <= 10) //若是剩餘環數小於10,只要最後一槍打sum環就能夠了
		{
			for(unsigned i = 0; i < result.size(); i++)
				cout<<result[i]<<' ';
			cout<<sum<<endl;
			(*total)++;
			return;
		}
		else
			return;
	}
	for(unsigned i = 0; i <= 10; i++) //命中0-10環
	{
		result.push_back(i);
		ShootProblem_Solution1(number-1, sum-i, result, total); //針對剩餘環數遞歸求解
		result.pop_back();
	}
}
//提供的公共接口
void ShootProblem(int number, int sum)
{
	int total = 0;
	vector<int> result;
	ShootProblem_Solution1(number, sum, result, &total);
	cout<<"total nums = "<<total<<endl;
}
相關文章
相關標籤/搜索