字母排列(定长)

问题:有一个长度为 nLength 的字符串 S,不考虑 S 里面有相同的字符,将 S 中出现的字符排列成长度为 nLength 的字符串,打印出所有的排列字符串(包括原串)。

如,给定 abc ,则要输出 abc,acb,bac,bca,cab,cba。

为了得到所有排列,我们采用首字符交换法,即,把后面的字符逐个与首字符交换,再把后面的字符串当作一个子问题,于是可以用递归解决,如图1所示。


图1 递归解法

给出源码:

/*
	输入字符串:inputWord
	每一次递归到最深层时组合形成的完整字符串:outputWord
	要组合的字符串的开始/结束位置:begin(inclusive) / end(exclusive)
*/
void combineAndShow(char *inputWord,char *outputWord,int begin,int end)
{
	const char FIRST_CHAR = *(inputWord + begin);
	const char LAST_OUTPUT_CHAR = *(outputWord + begin);
	char &firstCh = *(inputWord + begin);
	char &lastOutPutCh = *(outputWord + begin);
	*(outputWord + begin + 1) = '\0';
	
	if(begin == end - 1)
	{
		printf("%s\r\n",outputWord);
		return;
	}
	
	
	//原首字母不动  // 将 i = beging 的情况单独处理是因为此时 firstCh 和 currentCh 是一个变量的两个引用
	                // 使用 a ^= b;b ^= a;a ^= b;的方法用于两边是同一个变量时会有错。
	                // 如 int a = 1;  a ^= a; a ^= a; a ^= a; 这样做会将 a 置为 0;
	lastOutPutCh = firstCh;
	combineAndShow(inputWord,outputWord,begin + 1,end);
	lastOutPutCh = LAST_OUTPUT_CHAR;
	*(outputWord + begin + 1) = '\0';
	

	
	for (int i = begin + 1;i != end - 1; ++ i)	
	{
		char &currentCh = *(inputWord + i);
		
		lastOutPutCh = currentCh;

		//将 currentCh 交换到字符串的首位置
		firstCh ^= currentCh;
		currentCh ^= firstCh;
		firstCh ^= currentCh;

		combineAndShow(inputWord,outputWord,begin + 1,end);
		
		//之后要交换回来
		lastOutPutCh = LAST_OUTPUT_CHAR;
		*(outputWord + begin + 1) = '\0';
		firstCh ^= currentCh;
		currentCh ^= firstCh;
		firstCh ^= currentCh;
	}

}

int main()
{
	freopen("C:\\outPut.txt","w",stdout);
	char str [] = {"12345678"}; //这里不能用字符串常量 : char *str = "12345678";
	                            //因为后面的算法会更改这个字符串,所以要分配空间
	char outPut[9] = {0};
	combineAndShow(str,outPut,0,9);
	return 0;
}

而一般情况下,递归的复杂度比动态规划复杂度高不少,因为,递归调用有比较大的时间耗费,而且也会有很深的函数栈,耗费不少的内存空间。而迭代没有函数递归调用的负担,它保存 i - 1 次计算结果并直接用于第 i 次的计算。 所以迭代的效率一般高于递归。我们把这个解决办法换为迭代的。

为此,另外先写了一篇文章,《算法之递归,迭代,动态规划,分冶》里面归纳了递归与迭代的本质不同,既然知道不同,就知道如何去转换了。在转换为迭代的过程中,我们采用最简单的转换方法,从底向上迭代,将第 i- 1 轮的计算结果(设为 F(i-1))缓存下来用于本轮(第 i 轮)构成结果 F(i)。

定义 i 为字符串下标,设字符串为 S,S[i] 表示取 S 的第 i 个字符。

F(0) 表示字符串为 S[0] 组合的结果,

F(1) 表示字符串为 S[0],S[1] 组合的结果,

F(2) 表示字符串为 S[0],S[1],S[2]组合的结果,

F(3) 表示字符串为 S[0],S[1],S[2],S[3] 组合的结果,

每一个 F 都是一个集合(也可以称为字符串数组)。

模拟由 F(2) 得到 F(3):

1.对于 F(2) 中的每一个字符串 T 有以下步骤2:

2.将 S[3] 追加到 T 的末尾。新得到的 T 为 T'。对 T' 有步骤3

3.遍历 T' 的每一个字符,将它与 T' 最末尾的字符交换。每次交换得到一个新字符串加入 F(3)

经过这三步,便能求出 F(3)。

其实这种转换是不节实际的,因为这需要很大的空间来存放结果。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值