现在的位置: 首页 > 综合 > 正文

使用递归算法解决字符的组合问题

2013年08月06日 ⁄ 综合 ⁄ 共 2576字 ⁄ 字号 评论关闭

    前几天到ChinaUnix的Python版去闲逛,在http://bbs.chinaunix.net/viewthread.php?tid=757345&extra=page%3D2 发现了这个问题,原文如下:

    把一个字符串中所有字符的所有可能的组合打印出来(字符串中没有重复的字符),不考虑字符顺序('123'和'312'是一样的)
 

bleem1998已经写出了该问题的非递归的求解方法,基本思想就是按照组合的字符串的长度依次穷举.原来很容易懂.但是实现起来稍微麻烦一些,所以我找了一个比较简单的方法.

    我所采用了递归的方法来解决这个问题,也就是什么说的分冶策略,把一个字符的组合问题拆分成两个规模比较小的字符的组合问题,从而导致递归求解.拆分的方法如下:

    对于任意的满足题目条件的字符串,总是可以拆分成两个字符串:第一个字符a,和剩余的字符构成的字符串b. 例如字符串"12345"可以拆分成两个字符串"1"和"2345",对于原来的问题那么就转化成了:求字符串a中的所有字母的组合的字符串集合A,以及字符串b中的字符的组合的字符串的集合B,然后把这两个集合进行一个笛卡儿积运算A×B,把两个集合的字符串拼接起来构成一个新的字符串集合C,从而C就是求得所得的结果.对于集合A,显然就只要2个元素,因为a只有一个字符;而字符b相对于原来的字符串长度已经减少了1,同原来的问题的性质一样,因此可以递归调用原来的函数求解.

     整个求解的过程可以用一个二叉树来表示,而对于问题的描述可以用一个二元组(prefix,src)来表示,prefix表示已经拆分了的字符串,src表示有待于拆分的字符串.

    下面是规模为3的时候的二叉树表示图,问题的答案就是这个完全二叉树的叶接点的prefix字符串所组成的集合,包括空字符串,显然一共有2n个字符串(n表示字符串的长度)

    下面是我用Python写的一个求解的程序,就只有几行代码,很简洁,我确实被Python所折服了

def permute(str,prefix):
    if len(str) == 0:
        print prefix,
        return
    permute(str[1:],prefix)
    permute(str[1:],prefix+str[0])

permute('123','')

这是解答的结果,和上面二叉树来表示完全一致

 3 2 23 1 13 12 123

由于用Python的人并不是很多,所以我也用C写了个程序作为参考

#include <iostream>
#include <cstring>

#define BUFFER_SIZE 255

using namespace std;

void permute(char *src, char *prefix)
{
    char buf[BUFFER_SIZE];
    int len = strlen(prefix);

    if (strlen(src) == 0)
    {
        cout<<prefix<<" ";
        return;
    }
    strcpy(buf,prefix);
    permute(src+1, prefix);
    prefix[len] = *src;
    prefix[len+1] = '/0';
    permute(src+1, prefix);
}

int main(int argc,char argv)
{
    char buf[BUFFER_SIZE] = "";
    permute("12345",buf);
    return 0;
}

 

相比之下,这个程序看起来要复杂得多,远不入Python那么简洁,这就是Python的魅力所在.

另外stone.zh也提供了一个简洁的方法,不过我现在也没有看懂,因为我不知道yield到底是什么做什么用的,不知道有没有知道,顺便告诉一下,谢过了先.

def permute(str):
    for i in str:
        yield i;
        for j in permute(str[str.index(i)+1:]):
            yield i+j

for i in permute("12345"):
    print i,

 

与这个问题类似,"百度之星"中的初赛题目中也可以用这个方法来求解,只是这里的组合问题是菜的组合,而且组合的元素的个数也固定了.有兴趣可以试试,题目如下

出处见http://star.baidu.com/data/chusai/q2.html

饭团的烦恼

“午餐饭团”是百度内部参与人数最多的民间组织。
同一个部门的、同一所大学的、同一年出生的、使用同一种型号电脑的员工们总是以各种理由组织各种长期的、临时的饭团。
 

参加饭团,不仅可以以优惠的价格尝到更加丰富的菜式,还可以在吃饭的时候和同事们增进感情。
但是,随着百度的员工越来越多,各个饭团的管理变得繁杂起来。特别是为了照顾员工们越来越挑剔的胃,饭团的点菜负责人的压力也越来越大。现在,这个任务就交给“百度之星”了,因为,你将要为所有的百度饭团设计一个自动点菜的算法
 

饭团点菜的需求如下:
1.经济是我们要考虑的一个因素,既要充分利用百度员工的午餐补助,又不能铺张浪费。因此,我们希望最后的人均费用越接近12元越好。
2.菜式丰富是我们要考虑的另一个因素。为简单起见,我们将各种菜肴的属性归结为荤菜,素菜,辛辣,清淡,并且每个菜只能点一次。
3.请谨记,百度饭团在各大餐馆享受8折优惠
 

输入要求:
1.输入数据第一行包含三个整数N,M,K(0<N<=16,0<M<=N,0<K<=12),分别表示菜单上菜的数目,饭团需要点的菜的数目,就餐的人数;
2.紧接着N行,每行的格式如下:
菜名(长度不超过20个字符) 价格(原价,整数) 是否荤菜(1表示是,0表示否) 是否辛辣(1表示是,0表示否);
3.第N+2行是 a b c d 四个整数,分别表示需要点的荤菜,素菜,辛辣,清淡菜的数目。例:
3 2 2
水煮鱼 30 1 1
口水鸡 18 1 1
清炖豆腐 12 0 0
1 1 1 1
样例:in.txt
 

输出要求:
对于每组测试数据,输出数据包含M+1行,前M行每行包含一个菜名(按菜名在原菜单的顺序排序)。第M+1行是人均消费,结果保留两位小数。例:
口水鸡
清炖豆腐
12.00
样例:out.txt

抱歉!评论已关闭.