加速计算字母表中没有重复的子串长度> 1的字符串的数量。 1

时间:2014-08-25 21:17:38

标签: string algorithm

因此,似乎没有人知道N的公式,因为大小为N的字母表中的字符串数量,每个字符串都没有重复的长度为> = 2的子字符串.De Bruijn序列的数量可用于提供下限。但是,如果我们想要计算尽可能大的N的确切数量呢?是否有编程技巧(对称性等)可用于计算适当大小N的此类字符串的数量?

1 个答案:

答案 0 :(得分:1)

利用明显的对称性是对字母身份进行置换,以便仅生成按字典顺序排列的最小代表。以下Python代码使用带有修剪的递归搜索。它不会走得太远,所以我不确定你是否认为它是“蛮力”。

可能有一颗银弹在等待,但我怀疑没有,在这种情况下推N就像用岩锤挖隧道一样。

import math

def nonrep(n, s=''):
    a = (ord(min(s)) if s else ord('a'))
    c = ((ord(max(s)) + 1) if s else a)
    total = (math.factorial(n) // math.factorial((n - (c - a))))
    for b in range(a, min((c + 1), (a + n))):
        t = (s + chr(b))
        if (t[(- 2):] not in s):
            total += nonrep(n, t)
    return total
for k in range(1, 12):
    print(nonrep(k))

下一步是通过使用的子串记忆。以下C ++ 11代码可以在具有大量内存的计算机上相当快地执行N = 6(2131886084545954033)。

#include <cstdio>
#include <unordered_map>

namespace {

static const int kN(6);

long long Count(std::unordered_map<unsigned long long, long long>* memo,
                unsigned long long used,
                int end) {
  auto got(memo->find(used));
  if (got != memo->end()) {
    return got->second;
  }
  long long total(1);
  for (int j(0); j != kN; ++j) {
    unsigned long long bit(1ULL << (j * kN + end));
    if (used < (1ULL << (j * kN)) && j != 0) {
      total += (kN - j) * Count(memo, used | bit, j);
      break;
    } else if ((used & bit) == 0) {
      total += Count(memo, used | bit, j);
    }
  }
  if (total >= 100) {
    (*memo)[used] = total;
  }
  return total;
}
}  // namespace

int main(void) {
  std::unordered_map<unsigned long long, long long> memo;
  std::printf("%lld\n", 1 + kN * Count(&memo, 0, 0));
}

在此之后的下一步可能是更对称性的破坏,例如,通过智能地将字母进一步置换为canonize used,但我认为这将是一个很长的途径N = 7.