具有重复字符的字符串排列

时间:2019-04-23 17:53:00

标签: algorithm permutation

我有字符串“ 0011”,并且希望所有组合都没有重复。 这意味着我想要一个带有两个“ 0”和两个“ 1”的组合的字符串; 例如:[0011,0101,0110,1001,1010,1100]

我尝试过,结果正是我所需要的。

private void permutation(String result, String str, HashSet hashset) {
        if (str.length()==0 && !hashSet.contains(result)){
            System.out.println(result);
            hashSet.add(result);
            return;
        }
        IntStream.range(0,str.length()).forEach(pos->permutation(result+ str.charAt(pos), str.substring(0, pos) + str.substring(pos+1),hashset));
    }

如果我删除HashSet,此代码将产生24个结果,而不是6个结果。

但是此代码的时间复杂度为O(n!)。
如何避免使用它创建重复的字符串并降低时间复杂度?

2 个答案:

答案 0 :(得分:0)

类似这样的东西可能比n快!即使在小n 这个想法是计算我们需要在结果项中有多少位,然后 遍历所有可能的值并仅过滤那些位数相同的值。只有一个1以及0和1的50%/ 50%时,它将工作相似的时间

function bitCount(n) {
        n = n - ((n >> 1) & 0x55555555)
        n = (n & 0x33333333) + ((n >> 2) & 0x33333333)
        return ((n + (n >> 4) & 0xF0F0F0F) * 0x1010101) >> 24
    }
    function perm(inp) {
        const bitString = 2;
        const len = inp.length;
        const target = bitCount(parseInt(inp, bitString));
        const min = (Math.pow(target, bitString) - 1);
        const max = min << (len - target);
        const result = [];
        for (let i = min; i < max + 1; i++) {
            if (bitCount(i) === target) {
                result.push(i.toString(bitString).padStart(len, '0'));
            }
        }
        return result;
    }
    
     const inp = '0011';
     const res = perm(inp);
     console.log('result',res);

P.s。我的第一个想法可能比上层代码快。但是鞋帮更容易实现

第一个想法是将字符串转换为int 并使用按位左移,但每次只能使用一位数字。它仍然取决于n。并且可以大于或小于上层解决方案。但是按位移位本身会更快。

example 
const input = '0011'
const len = input.length;

step1: calc number of bits = 2;
then generate first element = 3(Dec) is = '0011' in bin
step2 move last from the right bit one position left with << operator: '0101'
step3 move again: '1001'
step4: we are reached `len` so use next bit:100'1' : '1010'
step5: repeat:'1100'
step6: move initial 3 << 1: '0110'
repeat above steps: '1010'
step8: '1100'
it will generate duplicates so probably can be improved

希望有帮助

答案 1 :(得分:0)

最坏情况下的时间复杂度无法提高,因为字符串中不能有重复项。但是,如果是多集,我们可以修剪很多子树以防止重复。

关键思想是使用传统的回溯算法对字符串进行置换,但是如果字符先前已进行过交换以防止重复,则可以防止交换。

这是一个C ++代码段,可防止重复并且不使用任何内存进行查找。

bool shouldSwap(const string& str, size_t start, size_t index) {
  for (auto i = start; i < index; ++i) {
    if (str[i] == str[index])
      return false;
  }
  return true;
}

void permute(string& str, size_t index)
{
  if (index >= str.size()) {
    cout << str << endl;;
    return;
  }

  for (size_t i = index; i < str.size(); ++i) {
    if(shouldSwap(str, index, i)) {
      swap(str[index], str[i]);
      permute(str, index + 1);
      swap(str[index], str[i]);
    }
  }
}

正在运行demo。另请参阅解答hereDistinct permutations,以获取更多参考。

此外,请注意,此解决方案的时间复杂度为 O(n 2 n!)

  • O(n)用于打印字符串
  • O(n)遍历字符串以生成交换和重复发生。
  • 排列数量的
  • O(n!)个可能状态。