二进制字符串排列

时间:2011-10-19 03:45:41

标签: algorithm permutation

我在http://www.interviewstreet.com上遇到了一个问题。

  

Bob收到了Alice发送的长度为N的二进制字符串。他知道由于传输错误,最多K位可能已被破坏(因此被翻转)。但是,他也知道Alice打算传输的字符串不是周期性的。如果字符串不能表示为连接多次的较小字符串,则该字符串不是周期性的。例如,“0001”,“0110”不是周期性的,而“00000”,“010101”是周期性字符串。    现在他想知道爱丽丝有多少可能的字符串传输。

首先,我使用二项式定理进行了一些测试,并且通过使用它,我能够找到在给定字符串和多个损坏位的情况下可以表示字符串的多少种不同方式。我的第二步是找到一种方法来查找周期性字符串的数量。我看到这可以通过带有素数长度的字符串轻松完成。这是通过检查是否有足够的0或1来填充字符串仅用0或1来完成。

  

1111111或0000000

现在我使用的是一种纯粹的强力算法,当涉及到任何类型的大字符串时,它都不会削减它。是否有任何类型的组合技术可以指出我有助于解决这个问题?感谢。

6 个答案:

答案 0 :(得分:4)

Lior走在正确的轨道上。

长度N的字符串总数为2^N。其中一些是定期的。其他人不是。让我们调用周期性字符串A(N)的数量,以及非周期性字符串B(N)的数量。然后

A(N) + B(N) = 2^N

如果我们将长度为1的字符串定义为非周期性的,那么

A(1) = 0
B(1) = 2

我们现在假设N > 1。然后,长度为N的周期性字符串集包括周期性短于N的字符串。但是,对于长度为N的非周期性字符串集,情况并非如此。

长度为N的周期性字符串集由重复的非周期性长度字符串组成,这些长度为n的除数,包括长度为1的字符串。换句话说:

A(N) = sum(B(k) where k divides N and k < N)

例如:

A(6) = B(1) + B(2) + B(3)
     = (2^1 - A(1)) + (2^2 - A(2)) + (2^3 - A(3))
     = 2 + (4 - B(1)) + (8 - B(1))
     = 2 + 2 + 6
     = 10

所以我们现在有一个长度为N的周期性和非周期性字符串数的递推方程。

不幸的是,这对回答实际问题没有多大帮助。

这个问题意味着Bob收到了一个特定的字符串,他想知道有多少非周期性字符串与此字符串的最多K位不同。收到的字符串有C(N,K)个可能的突变,可能是传输的字符串。我们需要从中减去该集合中周期性字符串的数量。我们怎么能这样做?

首先,我们可以使用观察结果,任何周期性字符串都是非周期性字符串的重复。因此,对于每个潜在的句点kN的除数),我们会查看长度为k的子字符串。如果所有字符串与公共字符串的区别不超过K位组合,那么这个公共字符串是周期性字符串的基础,我们应该将计数减少一个。如果最小距离为dK - d > N/k,那么我们可以翻转每个子字符串中的各个位并仍然匹配,我们必须相应地减少计数。

答案 1 :(得分:3)

计算长度为 n 的非周期性字符串的数量:

  • 字符串总数:2ⁿ
  • 减去长度 n
  • 的周期性字符串数

计算长度 n 的周期性字符串数:

  • 查找 n 的所有除数,但n本身除外。例如:如果 n = 6 - 除数为1,2,3。

    (已讨论此方法here

  • 每个除数m可用于表示2 ^ m个周期字符串。例如

  • m = 1:{0,1} - 2 ^ 1个周期性字符串
  • m = 2:{00,01,10,11} - 2 ^ 2个周期性字符串
  • m = 3:{000,... 111} - 2 ^ 3个周期性字符串

    因此,对于n = 6,有2 + 4 + 8个周期性字符串

    正如Jeffery Sax和ANeves指出的那样,这些周期性字符串中的一些是相同的{例如0 * = 00 * = 000 *),所以我们必须消除它们。

    一个天真的方法是将所有这些字符串添加到一个关联容器中,该容器存储唯一元素(例如C ++中的set),并计算该容器中元素的数量。

    更好的优化是:对于m = m1,找到m1的所有除数,并避免添加已经存在于这些集合中的字符串的字符串。

下一步是计算任何这些周期性字符串和接收字符串之间的Hamming distance。如果它小于K-计数它。


编辑:针对大N和小K的更好解决方案

检查字符串是否为周期性的算法:

这可以通过将字符串与其自身的移位版本进行比较来实现。如果一个字符串与它的p位循环移位相同 - 那么它的循环为p。

这样一次循环移位一个字符串 - 我们可以检测它是否是最新的字符串(N / 2)字符串比较。

计算可能传输的字符串

如果没有非周期性传输要求,并且我们收到了N比特消息 - 可能已传输的可能消息数为C(N, 0) + C(N, 1) + C(N, 2) + ... + C(N, K)

对于N = 1000且K = 3:C(1000,0)+ C(1000,1)+ C(1000,2)+ C(1000,3)= 166,667,501

(这是原始字符串中切换0/1/2/3位的组合数。)

从这个数字,我们需要减少周期性字符串的数量 - 这是无法传输的。

例如:如果收到的字符串是000000且K = 2,我们可以确定发送的字符串不在{000000,001001,010010,100100}中。这些都是周期性的,汉明距离接收到的字符串最多为K.

C(6,0)+ C(6,1)+ C(6,2)= 1 + 6 + 15 = 22 其中4种组合是周期性的。

<强>算法:

我们将从收到的字符串开始,并生成上述所有组合。对于每种组合,我们将检查它是否是周期性的。如果是这样 - 我们将减少1。

答案 2 :(得分:2)

Lior和Jeffrey的答案构成了解决问题的基础,但是这篇文章中有待解决的一个有趣的问题是,如何有效地计算给定{{的周期性字符串的数量?{ 1}}。我的回答主要集中于此。

正如Lior和Jeffrey所指出的那样,在检查周期性的字符串时,我们只需要关心长度等于n的除数的子串。让我们看一个例子来看看我们能够实现这一目标的效率。

周期为m

的周期性字符串数

让输入字符串为

[input string, N, K]

让我们尝试找到周期为m = 4的周期性字符串的数量

第一位

如果我们比较每个子串的第一位,我们会发现它们都是0110 0011 0101 0001 s。如果我们假设所有后续位在所有子串中都相同,那么当执行0位翻转或4位翻转时,输入字符串可以是周期性的(周期为4)。

0

所以现在我们知道存在2个周期性的字符串,一个用于k = 0,另一个用于k = 4(假设所有子字符串中的后续位相同)。

第二位

现在让我们进入第二位。

0110 0011 0101 0001
^    ^    ^    ^
Number of 0s = 4
Number of 1s = 0
Number of bitflips to make all 0s to 1s = 4
Number of bitflips to make all 1s to 0s = 0


Number of periodic strings with period=4 for:
k = 0  =>  1
k = 4  =>  1

但等等,上面的陈述是真的IFF子串中当前位之前的所有位也有助于使字符串周期性。我们知道只有0110 0011 0101 0001 ^ ^ ^ ^ Number of 0s = 2 Number of 1s = 2 Number of bitflips to make all 0s to 1s = 2 Number of bitflips to make all 1s to 0s = 2 k=0,每个都会使字符串周期性地达到第1位。

因此,在将所有位计算到第2位时,我们可以在以下4种情况下获得周期性字符串:

k=4

第三位

继续第三位,我们会看到:

When previousK = 0:
    Flip the 2 `0`s to `1`s => new k = 2
    Flip the 2 `1`s to `0`s => new k = 2
When previousK = 4:
    Flip the 2 `0`s to `1`s => new k = 6
    Flip the 2 `1`s to `0`s => new k = 6

Number of periodic strings with period=4 for:
k = 2  =>  2
k = 6  =>  2

第四位

我们的第四位也是最后一位:

0110 0011 0101 0001
  ^    ^    ^    ^
Number of 0s = 2
Number of 1s = 2
Number of bitflips to make all 0s to 1s = 2
Number of bitflips to make all 1s to 0s = 2

We can get a periodic string in the following 4 cases:
When previousK = 2:
    Flip the 2 `0`s to `1`s => new k = 4
    Flip the 2 `1`s to `0`s => new k = 4
When previousK = 6:
    Flip the 2 `0`s to `1`s => new k = 8
    Flip the 2 `1`s to `0`s => new k = 8

Number of periodic strings with period=4 for:
k = 4  =>  4
k = 8  =>  4

我们现在已完成子字符串的最后一位,并且最后一步中各种k值的周期字符串总数为16。

递归关系和伪代码

让我们使用R [k]表示任何0110 0011 0101 0001 ^ ^ ^ ^ Number of 0s = 1 Number of 1s = 3 We can get a periodic string in the following 4 cases: When previousK = 4: Flip the 1 `0`s to `1`s => new k = 5 Flip the 3 `1`s to `0`s => new k = 7 When previousK = 8: Flip the 1 `0`s to `1`s => new k = 9 Flip the 3 `1`s to `0`s => new k = 11 Number of periodic strings with period=4 for: k = 5 => 4 k = 7 => 4 k = 9 => 4 k = 11 => 4 的周期字符串的当前计数,其从k1不等。对于每次迭代,我们需要查找上一次迭代的K值。

我们在每次迭代中最终做的是:

R[]

如果我们通过迭代从最低到最高的所有时段来执行上述过程,我们将获得所有周期性字符串的计数。将被选择的时段将是for offset = 0 to periodLen - 1 flip R[] and previousR[] for currentK = 1 to K R[currentK] = 0 numZeroes = 0 for (pos = offset; pos < n; pos += periodLen) if (str[pos] == '0') ++numZeros numOnes = (n / m) - numZeroes; for currentK = 1 to K if m == 0 R[currentK + numZeroes] = 1 R[currentK + numOnes] = 1 else if (previousR[currentK] > 0) R[currrentK + numZeroes] += previousR[currentK] R[currentK + numOnes] += previousR[currentK] totalPeriodicCount = 0 for currentK = 1 to K totalPeriodicCount += R[currentK] 的除数,其小于N。从最低到最高的过程将有一个优势,请阅读下一节了解详细信息。

计算可被较小期间整除的期间

仔细观察后,您会注意到我们还会多次计算某些周期性字符串。

例如。以下周期性字符串:

N

最终将被计为m = 4和m = 8

的一部分

0001 0001 0001 0001 表示使用上述伪代码获得的长度C[m]的周期内获得的周期性字符串的总数。设m表示使用长度为C[m']的句点获得的周期性字符串的实际计数,但不计算可以使用m形成的周期性字符串

更具体地说,如果当前时段periods < m的除数mtu小于v,那么我们就会这样做计算周期性字符串mtu的数量,即

v

在计算m的所有值的周期性字符串总数时,我们需要注意排除C[m] = C[t'] + C[u'] + C[v'] + C[m'] C[t]C[u],并且只考虑{{1} }。

自从我们计算C[v]时,我们已经计算了C[m']C[m]C[t']的值,我们只需查看它们并从C[u']中减去它们以获得C[v']。我将这个简单的部分作为练习留给读者。

C[m]

答案 3 :(得分:0)

checkperiodic(char [] string, int i, int length)
{
for(l=i; l <=length-i ; l=l+i)
if(strncmp(string, &string[l],i) != 0)
    return 0;
return 1;
}

N=strlen(string);
n= sqrt(N);
for(i=2;i <= n; i++)
{
if (N%i == 0)
    if(checkperiodic(string,i,N) || checkperiodic(string,N/i,N))
        break;
}

复杂度N ^ 1/2 * N *(除数之和)

答案 4 :(得分:0)

扩展Lior Kogan的回答Binary String permutations

设F(n)是可以为给定长度n形成的非周期性字符串的数量。

为了找到n,F(n)的非周期性字符串的数量,我们需要消除所有的F(m),其中m是n的所有排列的n的除数。

| n |     m | F(m)                     | answer |
| 1 |       | 2^1                      |      2 |
| 2 |     1 | 2^2-F(1)                 |      2 |
| 3 |     1 | 2^3-F(1)                 |      6 |
| 4 |   1,2 | 2^4-F(2)-F(1)            |     12 |
| 5 |     1 | 2^5 - F(1)               |     30 |
| 6 | 1,2,3 | 2^6 - F(1) - F(2) - F(3) |     54 |

天真的python实现

nperiodics = [0, 2] # for n = 1(0,1) and n = 2(01, 10)

def divisors(n):
    for d in xrange(1, n / 2 + 1):
        if n %  d == 0:
            yield d

def periodic(n, counts):
    return 2**n - sum(counts[d] for d in divisors(n))


for i in xrange(2, 10):
    nperiodics.append(periodic(i, nperiodics))

print nperiodics

输出:[0, 2, 2, 6, 12, 30, 54, 126, 240, 504]

答案 5 :(得分:0)

我用以下算法解决了这个问题,这是非常基本的。希望我解决的问题是你要问的答案。

如果我在一个字符串中有8个二进制字符,并且我想获得所有可能的排列,那么以下算法将正确地为您提供这些值。在其中,我特别跳过排列“00000000”,因为它对我没有价值:)。

以下代码在Ruby中:

size = 8
max_binary_value = (2 ** size) - 1  # In this case, 255

permutations = 1.upto(max_binary_value).map do |num|
  # This will zero-padd the string, ie: "%08d" % "10" # => "00000010",
  #   while to_s(2) will convert it to Base 2 in String form:
  "%0#{size}d" % num.to_s(2)
end