剑指27 字符串的全排列【一种易理解方法】

字符串的排列

http://www.nowcoder.com/questionTerminal/fe6b651b66ae47d7acce78ffdd9a96c7

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

思路:假设字符串一共有10个字符,按排列组合的常规思路(不考虑重复字符的情况),先确定第一个位置,然后第二个位置,...,那么第一个位置有10种可能,第二个位置有9种可能,依次类推,可知所有情况数为10!。具体来说,当第一个位置的字符确定后,则原字符串中该字符不应被后面位置所考虑,同理,当前面n个位置的字符确定后,则原字符串中的这些字符不能在被后面位置所考虑。那样怎样才能知道哪些字符已经被用过了呢?方法是通过一个数组来记录,该数组长度和原字符串相同,初始化值全为1,当字符串中某个位置的字符被加到子串中时,将其对应的数组元素置为0,当该字符又可以被加到字串中时,其对应数组元素再被置为1(像是开关)。另外,为了防止重复串的出现,对于新串的每个位置,通过set记录已被选择过的字符,后面该位置新添的字符不能出现set中。通过递归来实现每个位置的字符的选取(像是深度优先搜索),最后对结果数组进行排序,保证字典序。代码如下


function help(rst, str, strCur, accessList){
  if (strCur.length == str.length){
      rst.push(strCur);
      return;
  }
  let set = new Set();
  for (let i = 0; i < str.length; ++i) {
      if (accessList[i] && !set.has(str[i])){
          accessList[i] =0;
          set.add(str[i]);
          help(rst, str, strCur + str[i], accessList);
          accessList[i] = 1;
      }
  }
}
function Permutation(str)
{
    // write code here
    let rst = [];
    if (!str.length) return rst;
    let accessList = new Array(str.length).fill(1);
    help(rst, str, "", accessList);
    return rst.sort();
}

扩展

思考:上面的解法中,通过一个和原字符串相同长度的数组来记录当前哪些位置的字符已经被用过了,那么在不另开辟空间的条件下,有办法来避开前面已经用过的字符吗?一个直接的方法是当某个字符用过后,将删除改字符的新的字符串传入下次迭代中, 参考了“一颗闪闪发亮的马路星”, 代码如下:

function help(rst, str, strCur){
  if (!str){
      rst.push(strCur);
      return;
  }
  let set = new Set();
  for (let i = 0; i < str.length; ++i) {
      if (!set.has(str[i])){
          set.add(str[i]);
          let strNext = str.substring(0, i) + str.substring(i + 1);
          help(rst, strNext, strCur + str[i]);
      }
  }
}

function Permutation(str)
{
    // write code here
    let rst = [];
    if (!str.length) return rst;
    help(rst, str, "");
    return rst.sort();
}

扩展2

思考:上述第一个方法需要额外的数组来记录哪些位置的字符已经被用过,第二个方法通过构造新的字符串来保证后面位置的字符不会是前面用到的,那有可以直接在原字符串上操作的方法吗?即不需要额外的数组也不需要构造新字符串。牛客官方题解给出了方法,其本质和上述两个方法一致,就是在先确定前面位置的字符,后面位置的字符从剩余字符中选择组合,不同点在于,其通过交换两个字符位置的方法实现了所有可能字符的选择和避免已经选择的字符再被选择,可谓一举两得,非常精妙,c++中的swap函数可以直接交换字符串中的两个字符,js没法改变原字符串,所以转为数组来做,最后再将数组转为字符串。代码如下:

function help(rst, arr, pos){
  if (pos == arr.length - 1) {
    rst.push(arr.join(""));
    return;
  }

  let set = new Set();
  for (let i = pos; i < arr.length; ++i) {
    if (!set.has(arr[i])){
      set.add(arr[i]);
      let cache = arr[pos];
      arr[pos] = arr[i];
      arr[i] = cache;
      help(rst, arr, pos+1);
      arr[i] = arr[pos];
      arr[pos] = cache;
    }
  }
}

function Permutation(str)
{
    // write code here
    let rst = [];
    if (!str.length) return rst;
    let arr = str.split("");
    help(rst, arr, 0);
    return rst.sort();
}
全部评论

相关推荐

01-17 18:15
已编辑
门头沟学院 前端工程师
从上午约我面试然后他迟到,然后中午发消息打电话给我说重约面试时间,我就该意识到。【管理不规范,只是这家公司最小的问题】他妈一个不是技术的人来给我技术面。。。连vvue什么?连react是什么?连普通的HTTP请求是什么?这些东西都不懂的人来给我做技术面,我真的。。。。他妈浪费我40分钟。。一天面了三场,这家公司属实牛逼。不停的问我说上班下班时间谁来派任务公司在哪个区发展怎么样,公司的管理模式什么样,培养机制怎么样带教负责什么。如果出bug了谁来负责。我真的求你了别闹了。我答了15分钟,我已经很不想回答了。然后他就问了我一些很招笑的面试问题。问我前端框架架构设计怎么设计,Websocket可以实现SSE吗??最后还要我硬说,为什么我们公司没转正?为什么?为什么?我说我怎么知道。。这是领导决定,又不是我决定,他说让我分析一下。。。我真的草了,这个人是来搞我的吗?我最后问我说这个没有技术面,他说他就是技术面虽然我今天面的另外两家也很逆天。一个人不停的吹牛,自己100人的公司是全国前几,吹牛了一个小时。我中途几次想跑,真的是底下玩手机在听他那吹牛。。然后最后来了句说,我承诺的东西要实现哦,不然的话,公司会追责的,我我请问我承诺了什么?从头到尾也没有说让我承诺什么。而且我只是作为一个小小的前端卡拉咪,应届生。我要承担什么??好崩溃。。好崩溃的,一天面了三场。两家1000-9999的公司。面试官问的都很傻逼,甚至有些东西我问他估计都答不出来。。&nbsp;我这是在干嘛呀?浪费我一天的时间,我的奶奶。。我本来是抱着说我很菜,我要面试中发现自己的问题,现在来看他妈的这三场面试,面试本身就是问题。。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务