一阵子没打了, 竟然四题都做出来了.
5539. 按照频率将数组升序排序
题解
很暴力地构建 {数值: 频率}
字典, 再很暴力地根据频率构建 {频率: 降序数值列表}
. 最后根据频率升序输出.
垃圾代码
class Solution:
def frequencySort(self, nums: List[int]) -> List[int]:
mp = {}
for n in nums: mp[n] = mp.get(n, 0) + 1
mp2 = {}
for k, v in mp.items():
mp2.setdefault(v, []).append(k)
res = []
for k in sorted(mp2.keys()):
v = sorted(mp2[k], reverse=True)
for n in v:
for i in range(int(k)):
res.append(n)
return res
5540. 两点之间不包含任何点的最宽垂直面积
题解
Point.Y
是干扰项, 没用. 直接对每个 Point.X
排序, 找出相邻 Point.X
最大的间隔. 比第一题还水.
垃圾代码
class Solution:
def maxWidthOfVerticalArea(self, points: List[List[int]]) -> int:
lst = []
for p in points: lst.append(p[0])
lst.sort()
x = 0
for i in range(1, len(lst)):
x = max(x, lst[i] - lst[i - 1])
return x
5541. 统计只差一个字符的子串数目
题解
对 t
构造前缀树 trie
, 同时 trie
每一层保存从根节点到目前位置的字串个数. 然后以 s
的每个字符作为起点去递归匹配 trie
, 递归匹配时考虑 目前仍无字符不同
与 目前已有一个字符不同
两种情况, 返回所有子情况的个数的求和.
垃圾代码 (讲道理 Python 写 Trie 属实方便)
class Solution:
def countSubstrings(self, s: str, t: str) -> int:
trie = {}
for i in range(len(t)):
nxt = trie
for c in t[i:]:
nxt = nxt.setdefault(c, {})
nxt[''] = nxt.get('', 0) + 1
# print(trie)
res = 0
for i in range(len(s)):
res += self.cal(s, i, trie)
return res
def cal(self, s, idx, trie, diff=False):
res = 0
if idx >= len(s): return res
for c in trie:
if c == '': continue
next_trie = trie[c]
if c == s[idx]:
if diff: res += next_trie['']
res += self.cal(s, idx + 1, next_trie, diff)
else:
if diff: pass
else:
res += next_trie['']
res += self.cal(s, idx + 1, next_trie, True)
return res
5542. 通过给定词典构造目标字符串的方案数
题解
动态规划. 设 W o r d s Words Words 每个 W o r d Word Word 长度为 L w L_w Lw, T a r g e t Target Target 长度为 L t L_t Lt, N ( i , j ) N(i, j) N(i,j) 为 T a r g e t Target Target 第 i i i 个字符在 W o r d s Words Words 第 j j j 列出现次数. W o r d s Words Words 用前 j j j 个字符成功匹配 T a r g e t Target Target 前 i i i 个字符的可能情况数 D P ( i , j ) DP(i, j) DP(i,j) 为
D P ( i , j ) = D P ( i − 1 , j − 1 ) × N ( i , j ) + D P ( i , j − 1 ) DP(i, j) = DP(i - 1, j - 1) \times N(i, j) + DP(i, j - 1) DP(i,j)=DP(i−1,j−1)×N(i,j)+DP(i,j−1)
遍历时 j j j 从 i i i 而不是 0 0 0 开始, 因为大于等于 i i i 个字符才可能匹配上 i i i 个字. 同理 j j j 大于 L w − L t + i L_w - L_t + i Lw−Lt+i 时剩下的字符也不可能匹配得完, 即 j ∈ [ i , L w − L t + i ] j \in [i, L_w - L_t + i] j∈[i,Lw−Lt+i].
可以看到状态转移方程只跟 i i i 和 i − 1 i - 1 i−1 有关, 所以可以将 DP 空间从 L w × L t L_w \times L_t Lw×Lt 降为 L w L_w Lw, 遍历 j j j 时从后往前遍历以免覆盖 i − 1 i - 1 i−1 的状态.
其实还能进一步降 DP 空间降到 L w − L t + 1 L_w - L_t + 1 Lw−Lt+1, 因为 j j j 每次遍历的范围就是 [ i , L w − L t + i ] [i, L_w - L_t + i] [i,Lw−Lt+i]. 但是我懒.
不是很垃圾的代码
class Solution {
public int numWays(String[] words, String target) {
final int MOD = (int)1e9 + 7;
int lw = words[0].length(), lt = target.length();
long[] dpa = new long[lw];
long[][] n26 = new long[lw][26];
for (String w : words) {
for (int i = 0; i < lw; ++i) {
++n26[i][w.charAt(i) - 'a'];
}
}
// System.out.println(Arrays.deepToString(n26));
int c = target.charAt(0) - 'a';
for (int j = 0; j <= lw - lt; ++j) {
dpa[j] = n26[j][c];
if (j > 0) dpa[j] += dpa[j - 1];
}
// System.out.println(Arrays.toString(dpa));
for (int i = 1; i < lt; ++i) {
c = target.charAt(i) - 'a';
for (int j = lw - (lt - i); j >= i; --j) {
dpa[j] = dpa[j - 1] * n26[j][c];
dpa[j] %= MOD;
}
for (int j = i + 1; j <= lw - (lt - i); ++j) {
dpa[j] += dpa[j - 1];
dpa[j] %= MOD;
}
// System.out.println(Arrays.toString(dpa));
}
return (int)dpa[lw - 1];
}
}