字母组合(不定长)

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

如,给定 abc ,则要打印出 a,b,c,ab,bc,abc。

与 《字母排列(定长)》类似,最容易想到的也是递归法。难点是构造由 Fi 得到 Fi+1 的步骤。最直接的方法可能是设 Fi 为递归到 i 时的答案,它是一个集合(字符串数组),为了求解 Fi+1 ,必须将 Fi 存储起来,与字符 S[i+1] 发生作用,生成 Fi+1 。具体而言,为了由 Fi 和 S[i+1] 得到 Fi+1,需要遍历 Fi ,每一个字符串可以选择与字符 S[i+1] 发生组合,也可以不组合。这样,得到的 Fi+1 中的元素个数必定是 Fi 的 两倍。这个复杂度还是蛮高的,因为在递归中还要遍历结果。


那么,能不能优化一下呢。分析一下,我们保存着 Fi ,并赋予它一个意义:Fi 是字符串 S[0 ~ i] 组合的集合(也就是最终的组合结果)。如果它不是一个集合,而是一条组合的分支,只代表所有组合结果中的一种?

我们如果在递归函数中进行分支递归(选择与当前字符组合是一条分支,不组合是另一条分支),每一条分支刚好就是一种组合结果,那么,此时 Fi 就只表示一个字符串而已,并不表示字符串的集合,将这个字符串与一个字符组合是非常简单的,可以选择将字符追加到字符串末尾(代表将字符组合进来),或者不追加。还有一个问题,因为是分支递归,在当前这轮递归中,当前字符参与组合以及不组合应该会向下调用两个不同的过程,那么,如何构造“两个不同的递归过程”呢?也就是说,组合这个字符,和不组合这个字符对递归过程产生什么影响?有一种构造方法,把这个递归过程定义为:面对第 i 个字符,还需要组合 m 个字符的过程。这样的话,在本轮过程中,组合当前字符或者不组合便会影响下一轮递归的 m ,于是便有了这样的解决方法。

int totalCount = 0;

void branchRecursive(const char *sourceStr);

void doBranchRecursive(const char *sourceStr,int arrCounts,vector
   
   
    
    & chs)
{
	if( 0 == arrCounts)
	{
		vector
    
    
     
     ::iterator it = chs.begin();
		for (;it != chs.end();++ it)
		{
			++totalCount;
			printf("%c",*(it));
		}
		printf("\r\n");
		return;
	}
	if(0 == *sourceStr) //可能在某一轮递归中,一个字符也没有被组合进来
	                    //字符串遍历完了,此时不能再继续组合,也不能输出 chs,它是不完整的,因为并没有取得 arrCounts 个字符
	{
		return;
	}
	
	
	//排列当前字符
	char currentCh = *(sourceStr);
	chs.push_back(currentCh);
	doBranchRecursive(sourceStr + 1,arrCounts - 1,chs);
	
	chs.pop_back();
	doBranchRecursive(sourceStr + 1,arrCounts,chs);
}

void branchRecursive(const char *sourceStr)
{
	vector
     
     
      
       chs;
	int nLength = strlen(sourceStr);
	for (int i = 1;i <= nLength;++ i)
	{
		doBranchRecursive(sourceStr,i,chs);
	}
}

     
     
    
    
   
   


总结:分支递归是非常有用的一种递归方法,每轮递归(i 问题)给出的结果只是一条分支的结果。它利于计算 i + 1 问题的一个答案。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值