- Leetcode 3463. Check If Digits Are Equal in String After Operations II
- 1. 解题思路
- 2. 代码实现
- 题目链接: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的余数,这个简单关系如下:
n | mod 2 | mod 5 |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
2 | 0 | 2 |
3 | 1 | 3 |
4 | 0 | 4 |
5 | 1 | 0 |
6 | 0 | 1 |
7 | 1 | 2 |
8 | 0 | 3 |
9 | 1 | 4 |
但是,实际做题的时候我被卡在了这一步,后面没有搞定,但是问了一下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=0∑mni⋅pi=i=0∑mki⋅pi
则我们有:
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) Cnk≡i=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。