如何查找具有以下约束的二进制数的数量:

时间:2013-02-01 03:36:38

标签: math binary combinatorics

给定n的二进制数字计数和m的最大连续出现次数,找到不同的可能二进制数的数量。此外,最左边和最右边的位必须为1.

例如n = 5,m = 3。

计数是7: 10001 10011 10101 10111 11001 11011 11101

注意我们排除了11111,因为其中存在太多连续的1。

这是我最近的一个采访问题,一直困扰着我。我不想暴力检查每个数字的合法性,因为n可以是> 32。

2 个答案:

答案 0 :(得分:3)

如果二进制序列以“1”开头并且最多m个连续的“1”数字,那么我们调用二进制序列几乎有效

对于i = 1, ..., nj = 0, ..., m,让a(i, j)为长度为i且以j个连续“1”数字结尾的几乎有效序列的数量。

然后

  • a(1, 1) = 1a(1, j) = 0 for j != 1,因为“1”是长度为1的唯一有效序列。
  • 对于n >= 2j = 0,我们有a(i, 0) = a(i-1, 0) + a(i-1, 1) + ... + a(i-1, m),因为将“0”附加到任何几乎有效的长度为i-1的序列,会得到一个几乎有效的长度序列{{1以“0”结尾。
  • 对于in >= 2,我们有j > 0,因为将“1”附加到几乎有效的序列,a(i, j) = a(i-1, j-1)尾随的序列会给出一个几乎有效的长度序列{{1与i-1尾随的。{/ li>

最后,想要的数字是长度为j并且尾随“1”的几乎有效序列的数量,所以这是

i

写成C函数:

n

甚至可以改进存储要求,因为只需要矩阵f(n, m) = a(n, 1) + a(n, 2) + ... + a(n, m) 的最后一列。运行时复杂度为int a[NMAX+1][MMAX+1]; int f(int n, int m) { int i, j, s; // compute a(1, j): for (j = 0; j <= m; j++) a[1][j] = (j == 1); for (i = 2; i <= n; i++) { // compute a(i, 0): s = 0; for (j = 0; j <= m; j++) s += a[i-1][j]; a[i][0] = s; // compute a(i, j): for (j = 1; j <= m; j++) a[i][j] = a[i-1][j-1]; } // final result: s = 0; for (j = 1; j <= m; j++) s += a[n][j]; return s; }

答案 1 :(得分:2)

如果没有太多的组合洞察力,你可以用DP解决这个问题。让我们调用 left n,m right 长度为n的二进制字符串的数量,没有连续1的子字符串长于m,从头开始字符串左边,以字符串右边结束。显然,我们希望找到 1 n-2,m 1

关键观察是 left n,m right = left +'1'#< sub> n-1,m right + left +'0' n-1,m right

js中的一个简单实现(不确定它是否适用于小m,并且通常未经测试):

function hash(n,m) {

   return _('1',n-2);

   function _(left,n){
      if (m+1 <= left.length && left.lastIndexOf('0') <= left.length-m-2)
         return 0;
      if (n==0)
         return (m <= left.length &&
                 left.lastIndexOf('0') <= left.length-m-1 ? 0:1);
      return _(left+'1',n-1) + _(left+'0',n-1);
   }

}

hash(5,3); // 7

当然这比蛮力更有效,但是运行时复杂度仍然是指数级的,因此对于大的n值来说是不切实际的。