字符串(集合)的全排列和子集

来源:互联网 发布:局域网问卷调查软件 编辑:程序博客网 时间:2024/05/20 07:58
<p>
学过回溯法和分支限界法之后,觉得对递归有了很好地理解,但是真让写集合(字符串)的子集和全排列的code时,一时又感觉手足无措。
</p>
<p>
<br />

</p>
<p>
全排列
</p>
<p>
基本的想法:将字符串或集合的第一个元素固定,然后让其加上剩余串集合的子集。然后从第二位开始,跟第一个字符交换,然后再固定一个字符,加上剩余串的子集。交换的想法非常巧妙,然后利用递归的思想。
</p>
<p>
基本的code如下:
</p>
<p>
void swap(int *a, int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}


/*先以某个字符打头,计算剩余所有字符的子集,然后在*/
void total_arrange(int *data, int cur, int ed)
{
    if(cur >= ed){
        for(int i = 0; i < ed; ++i){
            cout<<data[i]<<" ";
        }
        cout<<endl;
    }
    else{
        for(int i = cur; i < ed; ++i){
            swap(&data[cur], &data[i]);//以data[i]打头,求剩余的子串的子集
            total_arrange(data, cur + 1, ed);
            swap(&data[cur], &data[i]);//以data[i]打头,求剩余的子串的子集
        }
    }
}
<br />

</p>
<p>
集合的子集基本的思想:可以从二项分布的思想去理解问题,n个特征,每个特征只有两种选择0-1,选择与否。从递归树的角度来看,是一个具有n+1层的完全二叉树,每个结点的分叉都有两种可能,0-1,即加入or不加入,这样每个叶子结点(共有2^n个叶子结点)都可以表示成长度为长度为n的0-1串。
</p>
<p>
具体的实现如下:
</p>
<p>
/*想象递归树,左边是加入,右边不加入,树的深度就是字符串的长度,
    每个叶子结点就是一个0-1串,0代表不加入,1代表加入*/
void subset(const string str, bool *flag, int level)
{
    if(level >= str.length()){//到达底层
        for(int i = 0; i < str.length(); ++i){
            if(flag[i] == true){
                cout<<str[i];
            }
        }
        cout<<endl;
    }
    else{
        for(int i = level; i <= str.length(); ++i){//循环的结束条件要加等号,否则只打印部分结果
            flag[i] = false;//选择分支
            subset(str, flag, i + 1);//深度遍历
            flag[i] = true;//回溯到上一层
        }
    }
}<br />

</p>
<p>
递归进行过程中,需要建立一个递归栈,存储程序跳转时的位置,如果递归层数一深,很容易出现栈溢出。根据上述的想法,我们可以用一个整数的每一位对应字符串中的每一位,0表示不加入,1表示加入。然后遍历一下这个整数的范围。
</p>
<p>
具体的实现如下:
</p>
<p>
/*一个字符串加入与否,可以使用0-1模拟*/
void subset1(const string str)
{
    unsigned int len = str.length();
    unsigned int slen = (1 << len) - 1;
    for(unsigned int i = 0; i <= slen; ++i){
        for(unsigned int j = 0; j < len; ++j){
            if(i & (0x01 << j)){
                cout<<str[j];
            }
        }
        cout<<endl;
    }
}
<br />

</p>
<p>
当然这样的做法是有一定问题的,unsigned int最多表示32位的整数,这样输入字符串的长度不能超过32,当然可以改成long long类型,最多也只能达到64位。不过可以自己定义进位表示,定义两个变量,一个表示另一个的高位,如果地位溢出,高位在操作。
</p>
0 0
原创粉丝点击