字符串排列与组合

学习的博客
1. 字符串排列
给定一个字符串,打印出该字符串中字符的所有排列,这里有个注意点就是字符串中是否有重复字符,例如abc,它的全排列有abc, acb, bac, bca, cab, cba; 但是对于字符串abb,它的全排列只有abb,bab,bba。
对于没有重复字符的字符串

  • 首先,我们固定第一个字符a,求后面两个字符bc的排列。
  • 当两个字符bc排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列。
  • 现在是把c放在第一个位置的时候了,但是记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍是和原先处在第一个位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处于第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列。
    对于有重复字符的字符串
    由于全排列就是从第一个数字起,每个数分别与它后面的数字交换,我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换了。例如abb,第一个数与后面两个数交换得bab,bba。然后abb中第二个数和第三个数相同,就不用交换了。但是对bab,第二个数和第三个数不同,则需要交换,得到bba。由于这里的bba和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。
    换种思维,对abb,第一个数a与第二个数b交换得到bab,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数,所以第一个数就不再用与第三个数交换了。再考虑bab,它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕!
    这样,我们得到在全排列中去掉重复的规则:
    去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换。
    以下是这两种情况下的java实现
package sxd.learn.java.questions;

public class StringPermutation {
    private int counts;

    public static void main(String[] args) {
        new StringPermutation().run();
    }

    public void run() {
        StringBuilder str = new StringBuilder("abb");
        permutation(str);
        permutation2(str);
    }

    /**
     * 字符串全排列,字符串中没有重复字符
     */
    public void permutation(StringBuilder str) {
        if (str == null)
            return;
        permutation(str, 0);
    }

    private void permutation(StringBuilder str, int begin) {
        if (begin == str.length() - 1) {
            System.out.println(++counts + ":" + str);
        } else {
            for (int i = begin; i < str.length(); i++) {
                exchange(str, begin, i);
                permutation(str, begin + 1);
                exchange(str, begin, i);

            }
        }
    }

    /**
     * 字符串全排列,字符串中有重复字符
     * 去重的全排列是从第一个数字起每个数分别与它后面非重复出现的数字交换
     */
    public void permutation2(StringBuilder str){
        if(str != null){
            permutation2(str, 0);
        }
    }

    private void permutation2(StringBuilder str, int begin) {
        if (begin == str.length() -1) {
            System.out.println(counts++ + ":" + str);
        } else {
            for (int i = begin; i < str.length(); i++) {
                if (isExchange(str, begin, i)) {
                    exchange(str, begin, i);
                    permutation2(str, begin + 1);
                    exchange(str, begin, i);
                }
            }
        }
    }

    private boolean isExchange(StringBuilder str, int begin, int end) {
        for (int i = begin; i < end; i++) {
            if (str.charAt(i) == str.charAt(end))
                return false;
        }
        return true;
    }

    private void exchange(StringBuilder str, int i, int j) {
        char temp = str.charAt(i);
        str.setCharAt(i, str.charAt(j));
        str.setCharAt(j, temp);
    }

}
  1. 字符串组合
    给定一个字符串,求该字符串中字符的所有组合。例如,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc。

假设我们想在长度为n的字符串中求m个字符的组合。我们先从头扫描字符串的第一个字符。针对第一个字符,我们有两种选择:第一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;第二是不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符。这两种选择都很容易用递归实现。

package sxd.learn.java.questions;

import java.util.Vector;

public class StringCombination {

    private int counts;

    public static void main(String[] args) {
        new StringCombination().run();
    }

    public void run(){
        combination(new StringBuilder("abc"));
    }

    public void combination(StringBuilder str){
        if(str == null)
            return;

        Vector<Character> vector = new Vector<>();
        for (int i = 1; i <= str.length(); i++) {
            combination(str, i, 0, vector);
        }
    }

    private void combination(StringBuilder str, int number, int start, Vector<Character> vector){
        if(number == 0){
            System.out.print(++counts + ":");
            for (Character character : vector) {
                System.out.print(character);
            }
            System.out.println();
            return;
        }

        if(start == str.length())
            return;

        vector.add(str.charAt(start));
        combination(str, number - 1, start + 1, vector);

        vector.remove(vector.size() - 1);
        combination(str, number, start + 1, vector);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值