【问题标题】:How to determine the number of possible combinations of letters that contain a degenerate substring如何确定包含退化子字符串的字母的可能组合数
【发布时间】:2019-09-29 17:16:39
【问题描述】:

几天来我一直在绞尽脑汁想出一个级数或封闭式方程来解决以下问题:

具体来说:给定所有长度为 N 的字符串,这些字符串取自 L 个字母的字母表(以 'A' 开头,例如 {A, B}, {A , B, C}, ...),这些字符串中有多少包含与模式匹配的子字符串:'A',超过 1 个 not-'A','A'。该模式的标准正则表达式是A[^A][^A]+A

可能的字符串数量很简单:L^N。对于 NL 的小值,简单地创建所有可能的组合并使用正则表达式查找与模式匹配的子字符串也非常实用;在 R 中:

all.combinations <- function(N, L) {
    apply(
        expand.grid(rep(list(LETTERS[1:L]), N)),
        1,
        paste,
        collapse = ''
    )
}

matching.pattern <- function(N, L, pattern = 'A[^A][^A]+A') {
    sum(grepl(pattern, all.combinations(N, L)))
}

all.combinations(4, 2)
matching.pattern(4, 2)

我想出了以下方法,适用于 N

M <- function(N, L) {
    sum(
        sapply(
            2:(N-2),
            function(g) {
                (N - g - 1) * (L - 1) ** g * L ** (N - g - 2)
            }
        )
    )
}

不幸的是,这仅在 N

有什么建议吗?我也对程序解决方案持开放态度,只要它们不会因组合数量而爆炸(就像我上面的代码一样)。我希望能够计算 N 从 15 到 25 和 L 从 2 到 10 的值。

对于它的价值,这里是组合的数量,以及 N 和 L 的一些值的匹配组合,可以通过生成所有组合并进行正则表达式匹配来确定:

 N  L  combinations  matching
--  -  ------------  --------
 4  2            16         1
 5  2            32         5
 6  2            64        17
 7  2           128        48
 8  2           256       122
 9  2           512       290
10  2          1024       659
 4  3            81         4
 5  3           243        32
 6  3           729       172
 7  3          2187       760
 8  3          6561      2996
 9  3         19683     10960
10  3         59049     38076
 4  4           256         9
 5  4          1024        99
 6  4          4096       729
 7  4         16384      4410
 8  4         65536     23778
 9  4        262144    118854
10  4       1048576    563499

【问题讨论】:

  • 我猜你会对 math.stackexchange 更感兴趣
  • AAAAA 不应计算在内,因为它与模式不匹配(字母 A,后跟两个或多个非 A 字母,然后是另一个字母 A)。对于 N = 5 和 L = 2,匹配模式为 ABBAA, AABBA, BABBA, ABBBA, ABBAB。如果您熟悉正则表达式语法,则该模式将表示为A[^A][^A]+A

标签: r regex probability combinatorics longest-substring


【解决方案1】:

可以使用动态规划方法。

对于固定的L,令X(n) 为长度为n 且包含给定模式的字符串数,令A(n) 为长度为n 且包含给定模式并以A 开头的字符串数。

首先推导出A(n) 的递归公式。让我们按前 2-3 个字母对A(n) 中的所有字符串进行分组。 A(n) 中的字符串数:

  • “第二个字母A”是A(n-1)
  • “第二个字母非A,第三个字母是A”是A(n-2)
  • “第二个和第三个字母非 A”是(L^(n-3) - (L-1)^(n-3))。这是因为字符串“需要”在剩余的字母中至少有一个 A 才能被计算在内。

这样:

A(n) = A(n-1) + (L-1) * (A(n-2) + (L-1) * (L^(n-3) - (L-1)^(n-3)))

长度为n+1的字符串可以以A或非A开头:

X(n+1) = A(n+1) + (L-1) * X(n)
X(i) = A(i) = 0, for i <= 3

Python 实现:

def combs(l, n):
    x = [0] * (n + 1)  # First element is not used, easier indexing
    a = [0] * (n + 1)
    for i in range(4, n+1):
        a[i] = a[i-1] + (l-1) * (a[i-2] + (l-1) * (l**(i-3) - (l-1)**(i-3)))
        x[i] = a[i] + (l-1) * x[i-1]
    return x[4:]

print(combs(2, 10))
print(combs(3, 10))
print(combs(4, 10))

【讨论】:

    【解决方案2】:

    这可以被描述为一个状态机。 (为简单起见,x 是除A 之外的任何字母。)

    S0 := 'A' S1 | 'x' S0     // ""
    S1 := 'A' S1 | 'x' S2     // A
    S2 := 'A' S1 | 'x' S3     // Ax
    S3 := 'A' S4 | 'x' S3     // Axx+
    S4 := 'A' S4 | 'x' S4 | $ // AxxA
    

    统计长度为n的匹配字符串个数

    S0(n) = S1(n-1) + (L-1)*S0(n-1); S0(0) = 0
    S1(n) = S1(n-1) + (L-1)*S2(n-1); S1(0) = 0
    S2(n) = S1(n-1) + (L-1)*S3(n-1); S2(0) = 0
    S3(n) = S4(n-1) + (L-1)*S3(n-1); S3(0) = 0
    S4(n) = S4(n-1) + (L-1)*S4(n-1); S4(0) = 1
    

    尝试将S0(n) 减少到仅nL 会给出一个非常长的表达式,因此按原样计算递归函数是最容易的。

    对于非常大的n,这可以表示为矩阵表达式,并且可以有效地计算。

                                       n
                  [L-1  1   0   0   0 ]
                  [ 0   1  L-1  0   0 ]              T
    [0 0 0 0 1] × [ 0   1   0  L-1  0 ] × [1 0 0 0 0]
                  [ 0   0   0  L-1  1 ]
                  [ 0   0   0   0   L ]
    

    在 JavaScript 中:

    function f(n, L) {
      var S0 = 0, S1 = 0, S2 = 0, S3 = 0, S4 = 1;
      var S1_tmp;
      while (n-- > 0) {
        S0 = S1 + (L - 1) * S0;
        S1_tmp = S1 + (L - 1) * S2;
        S2 = S1 + (L - 1) * S3;
        S3 = S4 + (L - 1) * S3;
        S4 = S4 + (L - 1) * S4;
        S1 = S1_tmp;
      }
      return S0;
    }
    
    var $tbody = $('#resulttable > tbody');
    for (var L = 2; L <= 4; L++) {
      for (var n = 4; n <= 10; n++) {
        $('<tr>').append([
          $('<td>').text(n),
          $('<td>').text(L),
          $('<td>').text(f(n,L))
        ]).appendTo($tbody);
      }
    }
    #resulttable td {
      text-align: right;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <table id="resulttable">
    <thead>
    <tr>
    <th>N</th>
    <th>L</th>
    <th>matches</th>
    </tr>
    </thead>
    <tbody>
    </tbody>
    </table>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-05
      • 1970-01-01
      • 1970-01-01
      • 2012-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多