LeetCode 第 38 场双周赛

一阵子没打了, 竟然四题都做出来了.

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(i1,j1)×N(i,j)+DP(i,j1)

遍历时 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 LwLt+i 时剩下的字符也不可能匹配得完, 即 j ∈ [ i , L w − L t + i ] j \in [i, L_w - L_t + i] j[i,LwLt+i].

可以看到状态转移方程只跟 i i i i − 1 i - 1 i1 有关, 所以可以将 DP 空间从 L w × L t L_w \times L_t Lw×Lt 降为 L w L_w Lw, 遍历 j j j 时从后往前遍历以免覆盖 i − 1 i - 1 i1 的状态.

其实还能进一步降 DP 空间降到 L w − L t + 1 L_w - L_t + 1 LwLt+1, 因为 j j j 每次遍历的范围就是 [ i , L w − L t + i ] [i, L_w - L_t + i] [i,LwLt+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];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值