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