Recursion 字符串的全排列 String Permutation @CareerCup

字符串全排列的题目,还是套用模板,同样要注意ret的size问题会变化。而且与上一题不同的是不光是要在ret后面添加进新的组合,而且还要移除前面用过的组合。类似一个队列。


另外Java的substring中区间闭合情况是左闭右开 [ ) 所以想把一个字符串拆成两个区间就可以用这一句:

for(int j=0; j<=tmp.length(); j++){		
	// substring区间: [)
	ret.add(tmp.substring(0, j) + head + tmp.substring(j));
}



思路还是按照这里写的:http://hawstein.com/posts/8.4.html

思路一:

我们可以把串“abc”中的第0个字符a取出来,然后递归调用permu计算剩余的串“bc” 的排列,得到{bc, cb}。然后再将字符a插入这两个串中的任何一个空位(插空法), 得到最终所有的排列。比如,a插入串bc的所有(3个)空位,得到{abc,bac,bca}。 递归的终止条件是什么呢?当一个串为空,就无法再取出其中的第0个字符了, 所以此时返回一个空的排列。



思路二:

我们还可以用另一种思路来递归解这个问题。还是针对串“abc”, 我依次取出这个串中的每个字符,然后调用permu去计算剩余串的排列。 然后只需要把取出的字符加到剩余串排列的每个字符前即可。对于这个例子, 程序先取出a,然后计算剩余串的排列得到{bc,cb},然后把a加到它们的前面,得到 {abc,acb};接着取出b,计算剩余串的排列得到{ac,ca},然后把b加到它们前面, 得到{bac,bca};后面的同理。最后就可以得到“abc”的全序列。



package Recursion;

import java.util.ArrayList;

/**
 * Write a method to compute all permutations of a string
 * 
 *  写一个函数返回一个串的所有排列
 *
 */
public class S9_5 {

	public static void main(String[] args) {
		ArrayList<String> list = getPerms("abc");
		System.out.println("There are " + list.size() + " permutations.");
		for (String s : list) {
			System.out.println(s);
		}
	}

	// ========================================= 方法一
	/*
	 *	ABC:
	 *
	 *	A -> [BC]:
	 *		ABC, BAC, BCA
	 *
	 * A -> [CB]
	 * 		ACB, CAB, CBA
	 */
	public static ArrayList<String> getPerms(String str) {
		ArrayList<String> ret = new ArrayList<String>();
		rec(str, 0, ret);
		return ret;
	}
	
	public static void rec(String str, int start, ArrayList<String> ret){
		if(start == str.length()){
			ret.add("");				// Basecase总是要插入一个空字符串
			return;
		}
		
		rec(str, start+1, ret);	// 处理子问题
		int size = ret.size();		// 还是因为ret长度会变化所以记录在size变量中
		String head = str.substring(start, start+1);
		for(int i=0; i<size; i++){
			String tmp = ret.get(0);		// 从ArrayList头取出,并删除
			ret.remove(0);
			for(int j=0; j<=tmp.length(); j++){		// 加到ArrayList后头
				// substring区间: [)
				ret.add(tmp.substring(0, j) + head + tmp.substring(j));
			}
		}
	}
	
	//================================= 另一种方法二
	public static ArrayList<String> getPerms2(String str){
		ArrayList<String> ret = new ArrayList<String>();
		rec2("", str, ret);
		return ret;
	}
	
	/*
	 * ABC:
	 * 
	 * (A,BC)
	 * 		(AB,C)
	 * 			(ABC,)
	 * 		(AC,B)
	 * 			(ACB,)
	 * 
	 * (B,AC)
	 * 		(BA,C)
	 * 			(BAC,)
	 * 		(BC,A)
	 * 			(BAC,)
	 * 
	 * (C,AB)
	 * 		(CA,B)
	 * 			(CAB,)
	 * 		(CB,A)
	 * 			(CBA,)
	 *   
	 */
	// done: 已经完成部分,rest:尚未完成部分
	public static void rec2(String done, String rest, ArrayList<String> ret){
		if(rest.length() == 0){
			ret.add(new String(done));
			return;
		}
		
		// 遍历rest的所有字符,每次选择一种结合到done,rest去掉被选择的那个字符
		for(int i=0; i<rest.length(); i++){
			rec2(done+rest.charAt(i), rest.substring(0, i)+rest.substring(i+1), ret);
		}
	}

}


针对第二种方法,再补充一种用StringBuilder的方法,因为StringBuilder可以很方便地对字符串修改,特别记住以下StringBuilder函数:

public StringBuilder insert(int offset,
                   char c)

public StringBuilder deleteCharAt(int index)

public StringBuilder append(char c)


public static void main(String []args){
        String s = "abcdefg";
        rec(new StringBuilder(), new StringBuilder(s));
    }
     
    
    public static void rec(StringBuilder done, StringBuilder rest){
        if(rest.length() == 0) {
            System.out.println(done.toString());
            return;
        }
        
        for(int i=0; i<rest.length(); i++) {
            char c = rest.charAt(i);
            done.append(c);
            rest.deleteCharAt(i);
            rec(done, rest);
            rest.insert(i, c);
            done.deleteCharAt(done.length()-1);
        }
    }




PS1:Generate all the strings of n bits, Assume A[0...n-1] is an array of size n.

public class HelloWorld{

     public static void main(String []args){
        bin(3);
     }
     
     static char[] ss = {0,0,0};
     
     public static void bin(int len){
         if(len == 0) {
             System.out.println(new String(ss));
             return;
         }
         
         ss[len-1] = '0';
         bin(len-1);
         ss[len-1] = '1';
         bin(len-1);
     }
}

得出结果:

000
100
010
110
001
101
011
111

PS2: Generate all the string of length n drawn from 0...k-1

public class HelloWorld{

     public static void main(String []args){
        int len = 3; 
        // bin(len);
        k_bin(len, 5);
     }
     
     static char[] ss = {0,0,0};
     
     public static void bin(int len){
         if(len == 0) {
             System.out.println(new String(ss));
             return;
         }
         
         ss[len-1] = '0';
         bin(len-1);
         ss[len-1] = '1';
         bin(len-1);
     }
     
     
     public static void k_bin(int len, int k) {
         if(len == 0) {
             System.out.println(new String(ss));
             return;
         }
         
         for(int i=0; i<k; i++) {
             ss[len-1] = (char)(i+'0');
             k_bin(len-1, k);
         }
     }
}


000
100
200
300
400
010
110
210
310
410
020
120
220
320
420
030
130
230
330
430
040
140
240
340
440
001
101
201
301
401
011
111
211
311
411
021
121
221
321
421
031
131
231
331
431
041
141
241
341
441
002
102
202
302
402
012
112
212
312
412
022
122
222
322
422
032
132
232
332
432
042
142
242
342
442
003
103
203
303
403
013
113
213
313
413
023
123
223
323
423
033
133
233
333
433
043
143
243
343
443
004
104
204
304
404
014
114
214
314
414
024
124
224
324
424
034
134
234
334
434
044
144
244
344
444








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值