📌  相关文章
📜  能被 8 整除但不能被 3 整除的子串数(1)

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

介绍

这篇文章将介绍如何编写一个程序,来统计一个字符串中能被 8 整除但不能被 3 整除的子串数。

我们将采用动态规划的思路来解决这个问题。具体地,我们定义 $f_{i,j}$ 为以第 $i$ 个字符为结尾的长度为 $j$ 的子串能否被 8 整除但不能被 3 整除。显然, $f_{i,1}$ 等于该字符能否被 8 整除但不能被 3 整除,即 $f_{i,1}=[\text{该字符能被 8 整除但不能被 3 整除}]$。

状态转移方程

根据 $f_{i,1}$ 和 $f_{i,j-1}$,我们可以得到 $f_{i,j}$,由于 $f_{i,j}$ 只与 $f_{i,j-1}$ 有关,因此可以采用滚动数组来优化空间复杂度。具体地,状态转移方程为:

$$f_{i,j}=f_{i,j-1}\times 10+f(i-j+1,i)$$

其中 $f(l,r)$ 表示 $[l,r]$ 这个子串所表示的数字。上式的解释是:把 $[i-j+1,i]$ 这个子串添加到 $[i-j,i-1]$ 这个子串的后面,此时的子串为 $[i-j,i]$,判断它是否能被 8 整除但不能被 3 整除。

最终的答案为 $f_{1,n}+f_{2,n}+\cdots+f_{n,n}$(即所有以第 $i$ 个字符为结尾的子串的和)。

代码实现

下面是基于上述思路的代码实现,使用 Python 语言编写:

def solve(s):
    n=len(s)
    f=[0]*n
    ans=0
    for i in range(n):
        f[i]=(s[i]%8==0 and s[i]%3!=0)
        ans+=f[i]
    for j in range(2,n+1):
        for i in range(n-j+1):
            f[i]=f[i]*10+s[i+j-1]
            if j%3!=0 and f[i]%8==0:
                ans+=1
    return ans
示例

我们使用下面的示例来测试我们的程序:

assert solve("1506")==3
assert solve("228")==4
assert solve("2333")==0
assert solve("99991")==1
assert solve("993381")==2
总结

至此,我们提出的问题得到了解决。这篇文章中介绍了使用动态规划的思路来解决一个字符串中能被 8 整除但不能被 3 整除的子串数的问题,同时也给出了代码的实现。