记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步
目录
7/13 1291. 顺次数
顺次数的每一位都比前一位大 1,因此它一定是字符串 "123456789" 的连续子串。
可以按长度从小到大枚举子串,再转成整数判断是否在 low, high 范围内。
设当前长度为 L,则起点最多到 9-L。
把 "123456789"i:i+L 转成数字 x:
若 x < low,继续枚举;
若 low <= x <= high,加入答案;
若 x > high,后面的同长度子串只会更大,可以直接结束该长度枚举。
python
def sequentialDigits(low, high):
"""
:type low: int
:type high: int
:rtype: List[int]
"""
s = "123456789"
ans = []
for length in range(2, 10):
for i in range(0, 10 - length):
x = int(s[i:i + length])
if x < low:
continue
if x > high:
break
ans.append(x)
return ans
7/14 3336. 最大公约数相等的子序列数量
把每个元素有三种选择:
不选;
放入 seq1;
放入 seq2。
这样天然保证两条子序列不相交(同一个下标不会同时放进两边)。
定义二维 DP:
dpg1g2 表示处理到当前下标时,
seq1 的 GCD 为 g1、seq2 的 GCD 为 g2 的方案数。
其中 g=0 表示该子序列目前为空。
初始:
dp00 = 1(两边都没选任何元素)。
转移(处理元素 x):
从旧状态 (g1, g2) 出发,方案数为 v:
1 不选 x:保留到 (g1, g2)
2 x 放入 seq1:到 (gcd(g1, x), g2)
3 x 放入 seq2:到 (g1, gcd(g2, x))
遍历完后,答案是所有 g>=1 的 dpgg 之和。
因为要求两边都非空且 GCD 相等,g=0 不计入。
python
def subsequencePairCount(nums):
"""
:type nums: List[int]
:rtype: int
"""
from math import gcd
MOD = 10**9 + 7
m = max(nums)
# 预处理 gcd,减少转移常数
gtab = [[0] * (m + 1) for _ in range(m + 1)]
for a in range(m + 1):
for b in range(m + 1):
gtab[a][b] = gcd(a, b)
dp = [[0] * (m + 1) for _ in range(m + 1)]
dp[0][0] = 1
for x in nums:
ndp = [row[:] for row in dp] # 先继承"不选 x"
for g1 in range(m + 1):
row = dp[g1]
for g2 in range(m + 1):
v = row[g2]
if v == 0:
continue
ng1 = gtab[g1][x]
ng2 = gtab[g2][x]
ndp[ng1][g2] = (ndp[ng1][g2] + v) % MOD
ndp[g1][ng2] = (ndp[g1][ng2] + v) % MOD
dp = ndp
ans = 0
for g in range(1, m + 1):
ans = (ans + dp[g][g]) % MOD
return ans
7/15 3658. 奇数和与偶数和的最大公约数
前 n 个正奇数之和是 n^2。
前 n 个正偶数之和是 n(n+1)。
要求 gcd(n^2, n(n+1))。
提取公因子 n,得到:
gcd(n^2, n(n+1)) = n * gcd(n, n+1)。
因为 n 和 n+1 是连续整数,必互质,所以 gcd(n, n+1)=1。
因此答案直接是 n。
python
def gcdOfOddEvenSums(n):
"""
:type n: int
:rtype: int
"""
return n
7/16 3867. 数对的最大公约数之和
按题意先构造 prefixGcd:维护前缀最大值 mx,令 prefixGcdi = gcd(numsi, mx)。
再将 prefixGcd 升序排序,从两端配对,每次取最小未配元素和最大未配元素,累加它们的 gcd。
长度为奇数时中间元素不参与配对。
python
def gcdSum(nums):
"""
:type nums: List[int]
:rtype: int
"""
from math import gcd
n = len(nums)
prefix_gcd = [0] * n
mx = 0
for i, x in enumerate(nums):
mx = max(mx, x)
prefix_gcd[i] = gcd(x, mx)
prefix_gcd.sort()
ans = 0
for i in range(n // 2):
ans += gcd(prefix_gcd[i], prefix_gcd[n - 1 - i])
return ans
7/17 3312. 查询排序后的最大公约数
先统计 nums 中每个数的出现次数 cnt。
设 mx 为最大值,cntGd 表示所有数对中 gcd 恰好为 d 的个数。
从大到小枚举 d:先统计 nums 中 d 的倍数个数 v,则这些数两两组成的数对 gcd 一定是 d 的倍数,共有 v*(v-1)/2 对;再减去 gcd 为 2d,3d,... 的对数,得到恰好为 d 的对数。
对 cntG 做前缀和后,gcdPairs 升序序列中第 q 个位置就是第一个前缀和大于 q 的下标。
每个查询用二分查找即可。
python
def gcdValues(nums, queries):
"""
:type nums: List[int]
:type queries: List[int]
:rtype: List[int]
"""
from collections import Counter
from itertools import accumulate
from bisect import bisect_right
mx = max(nums)
cnt = Counter(nums)
cnt_g = [0] * (mx + 1)
for i in range(mx, 0, -1):
v = 0
for j in range(i, mx + 1, i):
v += cnt[j]
cnt_g[i] -= cnt_g[j]
cnt_g[i] += v * (v - 1) // 2
s = list(accumulate(cnt_g))
return [bisect_right(s, q) for q in queries]
7/18
python
7/19
python