记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步
目录
9/14 836. 矩形重叠
矩形用左下角和右上角表示,边或角相接不算重叠,必须有正面积交集。
投影到 x、y 轴后两段都严格相交才重叠:各自左边界小于对方右边界,下边界小于对方上边界。
python
def isRectangleOverlap(rec1, rec2):
"""
:type rec1: List[int]
:type rec2: List[int]
:rtype: bool
"""
return rec1[0] < rec2[2] and rec2[0] < rec1[2] and rec1[1] < rec2[3] and rec2[1] < rec1[3]
9/15 2472. 不重叠回文子字符串的最大数目
选出尽量多的互不重叠回文子串,每段长度至少为 k,优化目标是段数而不是总长度。
任意长度 >= k+2 的回文,去掉首尾后仍是回文且长度仍 >= k,贡献同样只有 1 段,却少占两个字符,后面更容易再放下别的段,所以选更长的不会更优。
不断去掉首尾,最终一定能缩成长度恰好为 k 或 k+1 的回文(分别对应两种奇偶),因此最优方案里每段只需这两种长度。
用 fi 表示前缀 s0:i 最多能选多少段。
算到位置 i 时三种选择取最大:不用以 i 结尾的回文,继承 fi-1;若长度为 k 的 si-k:i 是回文,则 fi-k+1;若长度为 k+1 的 si-k-1:i 是回文,则 fi-k-1+1。
python
def maxPalindromes(s, k):
"""
:type s: str
:type k: int
:rtype: int
"""
n = len(s)
f = [0] * (n + 1)
def pal(l, r):
return s[l:r] == s[l:r][::-1]
for i in range(k, n + 1):
f[i] = f[i - 1]
if pal(i - k, i):
f[i] = max(f[i], f[i - k] + 1)
if i > k and pal(i - k - 1, i):
f[i] = max(f[i], f[i - k - 1] + 1)
return f[n]
9/16 1621. 大小为 K 的不重叠线段的数目
n 个点排成一条线,要画恰好 k 条线段:每条至少盖住两个点,线段内部不能交叉,但两条可以头尾相接(端点重合)。
相邻两点之间是一条单位边,一共 n-1 条。一条线段就是连续占用若干条单位边,至少占用 1 条。
从左到右把整条线拆开看:左边空隙、第 1 段、中间空隙、第 2 段、......、第 k 段、右边空隙,这些部分加起来正好用完全部 n-1 条边。
空隙可以为 0,表示两段紧挨着、端点重合;每段占用的边数至少为 1。
把每段占用的边数都减 1,约束就变成「全部都可以是 0」,它们的和变成 n-1-k。这时一共 2k+1 个数(k 段 + k+1 个空隙),求非负整数解的个数,就是组合数 C(n+k-1, 2k)。
对 10^9+7 取模。
python
def numberOfSets(n, k):
"""
:type n: int
:type k: int
:rtype: int
"""
mod = 10**9 + 7
N = n + k - 1
K = 2 * k
num = den = 1
for i in range(K):
num = num * (N - i) % mod
den = den * (i + 1) % mod
return num * pow(den, mod - 2, mod) % mod
9/17 1477. 找两个和为目标值且不重叠的子数组
找两段互不重叠、和都等于 target 的子数组,让两段长度之和尽量小。
数组全是正数,从左往右扫时,以某个位置结尾、和为 target 的子数组最多只有一段,用前缀和就能定位它的左端点。
fi 表示只用前 i 个数时,已经找到的「和为 target 的最短子数组」有多短。
扫到 i 时若发现一段 j, i) 的和是 target,它左边完全不重叠的最短那段就是 f\[j,两段长度相加更新答案,同时用这一段更新 fi。找不到返回 -1。
python
def minSumOfLengths(arr, target):
"""
:type arr: List[int]
:type target: int
:rtype: int
"""
inf = 10**9
n = len(arr)
d = {0: 0}
f = [inf] * (n + 1)
s = 0
ans = inf
for i, v in enumerate(arr, 1):
s += v
f[i] = f[i - 1]
if s - target in d:
j = d[s - target]
f[i] = min(f[i], i - j)
ans = min(ans, f[j] + i - j)
d[s] = i
return -1 if ans > n else ans
9/18 1520. 最多的不重叠子字符串
某段里一旦出现某个字母,这段就必须覆盖这个字母在整个串里的全部出现位置。
合法段最多 26 种:从某个字母第一次出现的位置出发,把段里碰到的字母的最后出现位置都扩进来。
扩的时候如果碰到一个字母第一次出现在当前左端点左边,这段就不合法,交给以那个更早字母为起点的更大段去覆盖。
得到的区间要么不相交、要么一个套在另一个里面。要段数最多且总长度最短,按右端点从小到大排序,能选就选、重叠就跳过。
python
def maxNumOfSubstrings(s):
"""
:type s: str
:rtype: List[str]
"""
first = [len(s)] * 26
last = [-1] * 26
for i, ch in enumerate(s):
x = ord(ch) - 97
if first[x] == len(s):
first[x] = i
last[x] = i
intervals = []
for c in range(26):
if last[c] < 0:
continue
l, r = first[c], last[c]
i = l
ok = True
while i <= r:
x = ord(s[i]) - 97
if first[x] < l:
ok = False
break
r = max(r, last[x])
i += 1
if ok:
intervals.append((r, l))
intervals.sort()
ans = []
prev = -1
for r, l in intervals:
if l > prev:
ans.append(s[l:r + 1])
prev = r
return ans
9/19
python
9/20
python