Leetcode 3463. Check If Digits Are Equal in String After Operations II

1. 解题思路

这道题是题目Leetcode 3461的进阶版本,其实就是提高了对于算法效率的要求。

这道题我们实际就是要求s[:-1]s[1:]这两个字符串操作为最终一个字符之后的值是否相同。而将一个数字字符串按照给定操作变成最终一个字符,其原始字符串当中每一位参与的次数事实上就是一个杨辉三角,这个是很明显的。

因此,这里的问题事实上就变成了如何快速求出杨辉三角的第 n n n行的全部元素。考虑到我们最终是要考虑其个位数,因此事实上我们要求的是杨辉三角的第 n n n行上每一个元素的个位数,即其对 10 10 10的模。

有数学知识我们可以得到,杨辉三角的第 n n n行的第 k k k个元素事实上就是组合数 C n k C_n^k Cnk,但是当我们要求其对 10 10 10的模的时候,这个问题就变得有点复杂了。不过,考虑到 10 = 2 × 5 10 = 2 \times 5 10=2×5,然后由中国剩余定理,我们只需要分别求出 C n k C_n^k Cnk 2 2 2 5 5 5的余数,我们就必然可以一一对应的找出其对于 10 10 10的余数,这个简单关系如下:

nmod 2mod 5
000
111
202
313
404
510
601
712
803
914

但是,实际做题的时候我被卡在了这一步,后面没有搞定,但是问了一下deepseek之后,它给出了一个非常数学的解法,即使用Lucas定理进行快速求解。

我们首先给出Lucas定理的说明:

对于一个组合数 C n k C_n^k Cnk,要求其对于某一个素数 p p p的模,我们只需要将 n , k n, k n,k分别展开为 p p p进制数,即:
n = ∑ i = 0 m n i ⋅ p i k = ∑ i = 0 m k i ⋅ p i \begin{aligned} n &= \sum\limits_{i=0}^{m}n_i \cdot p^i \\ k &= \sum\limits_{i=0}^{m}k_i \cdot p^i \\ \end{aligned} nk=i=0mnipi=i=0mkipi
则我们有:
C n k ≡ Π i = 0 m C n i k i ( m o d   p ) C_n^k \equiv \mathop{\Pi}\limits_{i=0}^m C_{n_i}^{k_i} (mod\ p) Cnki=0ΠmCniki(mod p)

由此,我们就可以在任意 O ( l o g p n ) O(log_pn) O(logpn)的算法复杂度范围内求出任意组合数 C n k C_n^k Cnk关于 2 2 2 5 5 5的模了,进而,由上述模的对应关系,我们即可得到我们的最终答案。

2. 代码实现

给出python代码实现如下:

def comb_mod2(n, k):
    """ 计算 C(n, k) mod 2 """
    if k < 0 or k > n:
        return 0
    # 检查k的二进制位是否均为n对应位的子集
    return 0 if (k & ~n) else 1

# 预计算C(ni, ki) mod5的值(ni和ki为0-4)
mod5_comb_table = [
    [1, 0, 0, 0, 0],  # n=0
    [1, 1, 0, 0, 0],  # n=1
    [1, 2, 1, 0, 0],  # n=2
    [1, 3, 3, 1, 0],  # n=3
    [1, 4, 1, 4, 1],  # n=4(C(4,2)=6 mod5=1,C(4,3)=4 mod5=4)
]

def comb_mod5(n, k):
    """ 计算 C(n, k) mod 5 """
    result = 1
    while n > 0 or k > 0:
        ni = n % 5
        ki = k % 5
        if ki > ni:
            return 0
        # 查预计算表获取C(ni, ki) mod5
        result = (result * mod5_comb_table[ni][ki]) % 5
        n = n // 5
        k = k // 5
    return result

# 中国剩余定理映射表(a mod2,b mod5)→ 结果 mod10
crt_map = {
    (0, 0): 0,
    (0, 1): 6,
    (0, 2): 2,
    (0, 3): 8,
    (0, 4): 4,
    (1, 0): 5,
    (1, 1): 1,
    (1, 2): 7,
    (1, 3): 3,
    (1, 4): 9,
}

def get_element_mod10(i, j):
    """ 计算杨辉三角第i行第j个元素的个位数(索引从0开始) """
    if j < 0 or j > i:
        return 0
    a = comb_mod2(i, j)   # 计算模2
    b = comb_mod5(i, j)   # 计算模5
    return crt_map[(a, b)]

class Solution:
    def hasSameDigits(self, s: str) -> bool:
        n = len(s)
        weights = [get_element_mod10(n-2, i) for i in range(n-1)]
        
        def fn(s):
            ans = 0
            n = len(s)
            for i, ch in enumerate(s):
                digit = int(ch)
                ans = (ans + weights[i] * digit) % 10
            return ans
        
        return fn(s[:-1]) == fn(s[1:])

提交代码评测得到:耗时1443ms,占用内存19.2MB。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值