📜  门| GATE CS 1999 |问题23(1)

📅  最后修改于: 2023-12-03 14:58:18.902000             🧑  作者: Mango

门| GATE CS 1999 |问题23

这是一道GATE CS 1999的考题,是一道关于递推和组合计数的问题。

问题描述

给定一个长度为 $n$ 的字符串 $w$,其中字符集为 ${1, 2, \cdots , k}$,定义 $w$ 的“好的子串”为一个连续的子串,其中每个数字出现的次数都相同。例如,如果 $w$ 为“112223334”,则它的好的子串为“222”和“3334”。

现在,我们定义 $F(i,j)$ 为以 $i$ 结尾且长度为 $j$ 的好的子串的个数。写一个递推式来计算 $F(i,j)$。

解题思路

一般情况下,如果要递推计算某个问题的结果,那么我们通常要求出问题与子问题之间的关系。对于这道题,我们可以考虑如下两种情况:

  1. 如果 $w_{i-j+1}\cdots w_i$ 是一个好的子串,那么 $F(i,j) = F(i-1,j) + 1$。
  2. 如果 $w_{i-j+1}\cdots w_i$ 不是一个好的子串,那么 $F(i,j) = 0$。

注意,用递推式计算 $F(i,j)$ 的时候,需要考虑 $j=k$ 的情况,即 $F(i,k)$ 表示一个长度为 $k$ 的“好的子串”,它一定是由相同的数字组成的。因此,在初始化的时候,$F(i,k)$ 的值应该等于 $1$ 或 $0$。

最终,我们的目标就是计算 $F(n,1)+F(n,2)+\cdots+F(n,k)$ 的值,它等价于计算长度为 $n$ 的字符串 $w$ 中包含多少个“好的子串”。

代码实现

以下是实现 $F(i,j)$ 的python代码,使用二维数组 $dp$ 记录。

def count_good_substrings(w, k):
    n = len(w)
    dp = [[0] * (k + 1) for _ in range(n + 1)]
    
    # 初始化,计算最后一个数字出现的前缀子串的个数
    for i in range(1, n + 1):
        dp[i][1] = 1 if i == 1 or w[i - 1] != w[i - 2] else 0
    
    # 递推计算 F(i,j)
    for i in range(2, n + 1):
        for j in range(2, k + 1):
            if w[i - 1] != w[i - j] or dp[i - 1][j] == 0:
                dp[i][j] = 0
            else:
                dp[i][j] = dp[i - 1][j] + 1
    
    # 计算所有长度为 j 的好的子串的个数之和
    return sum(dp[n][j] for j in range(1, k + 1))

以上就是本题的详细解答,如果还有不清楚的地方,欢迎在评论区提问。