面试题38:字符串的排列

问题1:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

思路:"abcde",第一轮,a与b交换,a与c交换,a与d交换,a与e交换,
              其中a每次都要回到原来的位置
              第二轮,a固定,"bcde"进行上述的情形,
              b与c交换,b与d交换,b与e交换,
              其中b每次都要回到原来的位置
              ...
              全部排列完毕
              
class Solution {
public:
    vector<string> Permutation(string str) {
        //可以用递归来做
        vector<string> array;
        if(str.size()==0)
            return array;
        Permutation(array, str, 0);
        sort(array.begin(), array.end());
        //按字典序输出各个字符串
        return array;
    }
     
    void Permutation(vector<string> &array, string str, int begin)
    //遍历第begin位的所有可能性
    {
        if(begin==str.size()-1)//最后一个字母了
            array.push_back(str);
        for(int i=begin; i<=str.size()-1;i++)
        {
            if(i!=begin && str[i]==str[begin])
            //有重复字符时,跳过
                continue;
            swap(str[i], str[begin]);
            //当i==begin时,也要遍历其后面的所有字符;
            //当i!=begin时,先交换,使第begin位取到不同的可能字符,
            //再遍历后面的字符
            Permutation(array, str, begin+1);
            //遍历其后面的所有字符;
             
            swap(str[i], str[begin]);
            //为了防止重复的情况,还需要将begin处的元素重新换回来
             
            //举例来说“abca”,为什么使用了两次swap函数
            //交换时是a与b交换,遍历;
            //交换时是a与c交换,遍历;(使用一次swap时,是b与c交换)
            //交换时是a与a不交换;
                
        }
    }
};

回溯法

/*
这是一个字符串排列问题,考虑使用回溯法
可以分为两部分来做
第一部分:求所有可能在第一个位置的字符,即把第一个字符与后面所有字符交换,包括自己和自己交换
第二部分:固定第一个字符,求后面所有字符的排列,即又回到第一部分
其中注意一个问题:如果第一个字符与后面某一个位置字符相同,则不用交换
*/
 
class Solution {
public:
    vector<string> Permutation(string str) {
        vector<string> all;
        if(str.size() == 0){
            return all;
        }
        string tmp;
        recur(str, tmp, all, 0);
        return all;
    }
    void recur(string str, string& tmp, vector<string> &all, int start){
        if(start < 0 || str.size() == 0){
            return;
        }
        if(str.size() == start){
            all.push_back(tmp);
            return;
        }
        for(int i = start; i < str.size(); i++){
            if(i != start && str[i] == str[start]){    //如果字符相同,不用交换
                continue;
            }
            swap(str[i], str[start]);
            tmp += str[start];
            recur(str, tmp, all, start + 1);
            tmp.pop_back();            //回溯法的关键
        }
    }
};

问题2:输入一个字符串,输出该字符串中字符的所有组合。举个例子,如果输入abc,它的组合有a、b、c、ab、ac、bc、abc。

 

//从头扫描字符串得到第一个字符,针对第一个字符,有两种选择

//把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;

//如果不把这个字符放到组合中去,则需要在剩下的n-1个字符中选取m个字符

 

//函数功能 : 从一个字符串中选m个元素
//函数参数 : pStr为字符串, m为选的元素个数, result为选中的
//返回值 :   无
void Combination_m(char *pStr, int m, vector<char> &result)
{
    	if(pStr == NULL || (*pStr == '\0'&& m != 0))
		return;
    	if(m == 0) //递归终止条件
    	{
		for(unsigned i = 0; i < result.size(); i++)
			cout<<result[i];
		cout<<endl;
		return;
	}
	//选择这个元素
	result.push_back(*pStr);
	Combination_m(pStr + 1, m - 1, result);
	result.pop_back();
	//不选择这个元素
	Combination_m(pStr + 1, m, result);
}
//函数功能 : 求一个字符串的组合
//函数参数 : pStr为字符串
//返回值 :   无
void Combination(char *pStr)
{
	if(pStr == NULL || *pStr == '\0')
		return;
	int number = strlen(pStr);
	for(int i = 1; i <= number; i++)
	{
		vector<char> result;
		Combination_m(pStr, i, result);
	}
}
/*
输入n个字符,那么形成的组合长度有1、2、... 、n
在n个字符中求长m的字符时,可以分成两部分:第一个字符和其余所有字符
如果组合中包含第一个字符,则在剩余字符中求m-1个字符
如果组合中不包含第一个字符,则在剩余字符中求m个字符
就可以用递归的方法求解
*/

class Solution
{
public:
    vector<string> combination(string str){
        vector<string> all;
	if (str.size() == 0) {
		return all;
	}
	string tmp;
	for (int i = 1; i <= str.size(); i++) {
		recur(str, tmp, 0, i, all);
	}
	return all;
    }
	
    void recur(string str, string &tmp, int start, int number, vector<string>& all){
        if (number == 0) {
	    all.push_back(tmp);
	    return;
	}
	if (start == str.size()) {
	    return;
	}
	if (number > str.size() - start) {
	    return;
	}
	tmp += str[start];
	recur(str, tmp, start + 1, number - 1, all);
	tmp.pop_back();
	recur(str, tmp, start + 1, number, all);
    }
};

问题3:n皇后问题,在一个N*N的棋盘上放置N个皇后,使其不能互相攻击(同一行、同一列、同一斜线上的皇后都会自动攻击),即任意两个皇后不得处于同一行、同一列、同意斜线上。

/*
经典的N皇后问题
假如N=4
我们可以定义一个数组column[4],数组中第i个数字表示位于第i行的皇后的列号
并将0,1,2,3分别放入数组中,放入的时候判断是否在同一列以及是否时一条斜线
它们肯定不会在同一行,因为数组下标0,1,2,3各不相同,因此所在行肯定不同
*/
class Solution{
public:
    int totalNQueens(int n) {
    	if (n < 0) {
    	    return 0;
    	}
    	vector<int> vec(n);
    	int sum = 0;
    	QueenCore(0, n, vec, sum);
    	return sum;
    }
    
    void QueenCore(int index, int n, vector<int> &vec, int &sum) {
    	if (index >= n) {
    	    sum++;
    	} else {
    	    for (int i = 0; i < n; i++) {
    	        vec[index] = i;
        		if (Diagonal(vec, index)) { 
            //判断是否在同一列或者是否在一条斜线上
        		    QueenCore(index + 1, n, vec, sum);
        		}
    	    }
    	}
    }

    bool Diagonal(const vector<int> &vec, int index) {
        //判断是否在同一列或者是否在一条斜线上
    	for (int i = 0; i < index; i++) {
    	    if ((abs(vec[i] - vec[index]) == abs(i - index)) || vec[i] == vec[index]) {             
    	        return false;
    	    }
    	}
    	return true;
    }
}

问题4:输入一个含有8个数字的数组,判断有没有可能把这8个数字分别放在正方体的8个顶点上,使得正方体上三组相对的面上的4个顶点的和都相等。

/*
简而言之,这就是一个全排列问题
把这8个数全排列,之后判断正方体的三个像对面是否相等即可
*/
class Solutin{
public:
    bool cubeVertex(vector<int> vec) {
        	if (vec.size() != 8) {
        		return false;
    	}
    	return cubeCore(vec, 0);
    }
    
    bool cubeCore(vector<int>& vec, int start) {
	if (vec.size() != 8 || start < 0) {
		return false;
	}
	bool result = false;
	if (start == vec.size() - 1) { //判断正方体的三个相对面是否相等
	    if (Sum(vec, 0, 1, 2, 3) == Sum(vec, 4, 5, 6, 7)
		&& Sum(vec, 0, 2, 4, 6) == Sum(vec, 1, 3, 5, 7)
		&& Sum(vec, 0, 1, 4, 5) == Sum(vec, 2, 3, 6, 7)) {
	        result = true;
	    }
	}
	else {
	    for (int i = start; i < vec.size(); i++) {
                if (i != start && vec[i] == vec[start]) {
		    continue;
		}
		swap(vec[i], vec[start]);
		result = cubeCore(vec, start + 1);
		if (result) {  //一旦为true,则直接break,后面无需在做排序
		    break;
		}
		swap(vec[i], vec[start]);
	    }
	}
	return result;
    }
    int Sum(vector<int> vec, int i, int j, int k, int l) {
	return vec[i] + vec[j] + vec[k] + vec[l];
    }
}

参考博客:

https://blog.csdn.net/wuzhekai1985/article/details/6643127#commentBox

 

https://blog.csdn.net/weixin_39953502/article/details/81297827#%E9%A2%98%E7%9B%AE%EF%BC%9A%E8%BE%93%E5%85%A5%E4%B8%80%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%EF%BC%8C%E6%B1%82%E5%AD%97%E7%AC%A6%E7%9A%84%E6%89%80%E6%9C%89%E7%BB%84%E5%90%88%E3%80%82%E4%BE%8B%E5%A6%82%EF%BC%9A%E8%BE%93%E5%85%A5abc%EF%BC%8C%E5%88%99%E5%AE%83%E4%BB%AC%E7%9A%84%E7%BB%84%E5%90%88%E6%9C%89a%E3%80%81b%E3%80%81c%E3%80%81ab%E3%80%81ac%E3%80%81bc%E3%80%81abc%E3%80%82%E5%85%B6%E4%B8%ADab%E5%92%8Cba%E5%8F%AA%E6%98%AF%E4%B8%80%E7%A7%8D%E7%BB%84%E5%90%88%E3%80%82

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值