剑指 offer:字符串的排列

题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

分析
这道题包括了重复字符的情况,会稍微复杂一点。先考虑无重复字符的情况。
如果会回溯算法的话,那么这道题会很简单。或者说深度优先遍历也可,因为其本质上也就是回溯算法。visit 数组表示第 i 个字符是否已经访问过。每次递归,对字符串中所有字符遍历一次。当访问至最后一个字符时,将整个 string 加入 vector<string> 中。举例来说,初始时,遍历 a,b,c,首先是 a,然后对 a 进行 递归,再遍历,a 已经访问过,b 没有,那么 a + b(字符合并),再递归,下一轮得到 c,递归到终止条件,回退;得到 a + c,再递归,得到 a + c + b。。。(过程真得很难说清楚,可参考深度优先遍历算法)。
对于重复字符的情况,我直接就暴力解法了,对已经生成的 vector<string> 排序, 然后删除重复元素,后来看了别人的博客,有更好的解法。如果有更好的办法烦请告诉我,谢谢。

void permutation(vector<string> &per, string str, string now, int* visit) {
	if (now.size() == str.size()) {
		per.push_back(now);
		return;
	}
	for (int i = 0; i < str.size(); ++i) {
		if (visit[i] == 0) {
			visit[i] = 1;
			permutation(per, str, now + str[i], visit);
			visit[i] = 0;
		}
	}
}

vector<string> Permutation(string str) {
	vector<string> per;
	if (str == "") {
		return per;
	}
	// 使用队列会更快
	int* visit = new int[str.size()];
	for(int i = 0; i < str.size(); ++i) {
		visit[i] = 0;
	}
	//vector<int> visit;
	permutation(per, str, "", visit);
	sort(per.begin(), per.end());
	for (int i = 1; i < per.size(); ++i) {
		if (per[i - 1] == per[i]) {
			per.erase(per.begin() + i - 1);
		}
	}
	return per;
}

这道题还有一种解法:将所有元素两两进行对换,所有的可能情况组合在一起,即为全排列。这个做法我自己想不到,是看了别人的博客才看懂的,还看了蛮久的。。。果然还是太菜(后面会贴上别人的博客)。
解释一下:从最后一个元素开始遍历,对每一个当前遍历到的元素,按照从前往后的顺序交换其和其后的每一个元素(如果不明白,就自己举个例子)。下面的代码蛮精妙的,我是想不到,非常值得学习。
去除重复值,有更好的做法,请看我下面贴的博客,我就不改了。。。
代码:

void permutation(vector<string> &per, string str, int begin) {
	if (begin == str.size() - 1) {
		per.push_back(str);
	}
	for (int i = begin; i < str.size(); ++i) {
		swap(str[begin], str[i]);
		permutation(per, str, begin + 1);
		swap(str[begin], str[i]);
	}
}

vector<string> Permutation2(string str) {
	vector<string> per;
	if (str == "") {
		return per;
	}
	permutation(per, str, 0);
	sort(per.begin(), per.end());
	// 去除重复值
	for (int i = 1; i < per.size(); ++i) {
		if (per[i - 1] == per[i]) {
			per.erase(per.begin() + i - 1);
		}
	}
	return per;
}

参考文献:
字符串的全排列和组合算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值