LeetCode 311 周赛

58 阅读5分钟

第一次AK!!!必须纪念一下!!

2413. 最小偶倍数

给你一个正整数 n ,返回 2n 的最小公倍数(正整数)。

示例

输入:n = 5
输出:10
解释:5 和 2 的最小公倍数是 10 。

思路

hello world级别的签到题。我刚读完题还想着要求一下最大公约数gcd,然后发现其实gcd都不用求。

class Solution {
    public int smallestEvenMultiple(int n) {
        if (n % 2 == 0) return n;
        return 2 * n;
    }
}

2414. 最长的字母序连续子字符串的长度

字母序连续字符串 是由字母表中连续字母组成的字符串。换句话说,字符串 "abcdefghijklmnopqrstuvwxyz" 的任意子字符串都是 字母序连续字符串

  • 例如,"abc" 是一个字母序连续字符串,而 "acb""za" 不是。

给你一个仅由小写英文字母组成的字符串 s ,返回其 最长 的 字母序连续子字符串 的长度。

示例

输入:s = "abacaba"
输出:2
解释:共有 4 个不同的字母序连续子字符串 "a""b""c""ab""ab" 是最长的字母序连续子字符串。

思路

直接模拟就可以了,遍历并且对连续字符计数,遇到不连续的字符后清空计数。

class Solution {
    public int longestContinuousSubstring(String s) {
        int n = s.length();
        int ans = 1, cnt = 0, last = 0; //前一个字符 
        for (int i = 0; i < n; i++) {
            if (i == 0) {
                cnt++;
                last = s.charAt(i) - 'a';
            } else {
                int u = s.charAt(i) - 'a';
                if (u - 1 == last) {
                    cnt++;
                    last++;
                } else {
                    ans = Math.max(ans, cnt);
                    cnt = 1;
                    last = u;
                }
            }
        }
        return Math.max(ans, cnt);
    }
}

2415. 反转二叉树的奇数层

给你一棵 完美 二叉树的根节点 root ,请你反转这棵树中每个 奇数 层的节点值。

  • 例如,假设第 3 层的节点值是 [2,1,3,4,7,11,29,18] ,那么反转后它应该变成 [18,29,11,7,4,3,1,2]

反转后,返回树的根节点。

完美 二叉树需满足:二叉树的所有父节点都有两个子节点,且所有叶子节点都在同一层。

节点的 层数 等于该节点到根节点之间的边数。

示例


输入:root = [2,3,5,8,13,21,34]
输出:[2,5,3,8,13,21,34]
解释:
这棵树只有一个奇数层。
在第 1 层的节点分别是 3、5 ,反转后为 5、3 。

思路

先用层序遍历BFS将每一层的节点分别存起来,然后逐层遍历进行翻转。

class Solution {
    public TreeNode reverseOddLevels(TreeNode root) {
        List<List<TreeNode>> list = new ArrayList<>();
        Queue<TreeNode> q = new LinkedList<>();
        if (root != null) q.offer(root);
        while (!q.isEmpty()) {
            int n = q.size();
            List<TreeNode> layer = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                TreeNode x = q.poll();
                layer.add(x);
                if (x.left != null) q.offer(x.left);
                if (x.right != null) q.offer(x.right);
            }
            list.add(layer);
        }

        for (int i = 0; i < list.size(); i++) {
            if (i + 1 >= list.size()) break; // 不存在下一层了, 直接结束
            List<TreeNode> thisLayer = list.get(i);
            List<TreeNode> nextLayer = list.get(i + 1);
            boolean thisEven = i % 2 == 0;
            if (thisEven) {
                int iThis = 0, iNext = nextLayer.size() - 1;
                while (iThis < thisLayer.size()) {
                    TreeNode x = thisLayer.get(iThis++);
                    x.left = nextLayer.get(iNext--);
                    x.right = nextLayer.get(iNext--);
                }
            } else {
                int iThis = thisLayer.size() - 1, iNext = 0;
                while (iThis >= 0) {
                    TreeNode x = thisLayer.get(iThis--);
                    x.left = nextLayer.get(iNext++);
                    x.right = nextLayer.get(iNext++);
                }
            }
        }

        return root;
    }
}

今天再次读题后,发现只用翻转节点中的值,只用翻转节点中的值的话,节点本身可以完全不用动,代码可以写的更简单

class Solution {
    public TreeNode reverseOddLevels(TreeNode root) {
        Queue<TreeNode> q = new LinkedList<>();
        if (root != null) q.offer(root);
        int depth = 0;
        while (!q.isEmpty()) {
            int n = q.size();
            List<TreeNode> layer = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                TreeNode x = q.poll();
                layer.add(x);
                if (x.left != null) q.offer(x.left);
                if (x.right != null) q.offer(x.right);
            }
            if (depth % 2 == 1) {
                // 奇数层
                int i = 0, j = layer.size() - 1;
                while (i < j) swap(layer.get(i++), layer.get(j--));
            }
            depth++;
        }
        return root;
    }

    private void swap(TreeNode a, TreeNode b) {
        int t = a.val;
        a.val = b.val;
        b.val = t;
    }
}

该题也可以用深搜DFS来做,代码更短,大概思路是:镜像遍历左右两颗子树(y总牛逼!)

class Solution {

    private void dfs(TreeNode left, TreeNode right, int depth) {
        if (left == null) return ;
        if (depth % 2 == 1) {
            int t = left.val;
            left.val = right.val;
            right.val = t;
        }
        dfs(left.left, right.right, depth + 1);
        dfs(left.right, right.left, depth + 1);
    }

    public TreeNode reverseOddLevels(TreeNode root) {
        dfs(root.left, root.right, 1);
        return root;
    }
}

2416. 字符串的前缀分数和

给你一个长度为 n 的数组 words ,该数组由 非空 字符串组成。

定义字符串 word分数 等于以 word 作为 前缀words[i] 的数目。

  • 例如,如果 words = ["a", "ab", "abc", "cab"] ,那么 "ab" 的分数是 2 ,因为 "ab""ab""abc" 的一个前缀。

返回一个长度为 n 的数组 answer ,其中 answer[i]words[i] 的每个非空前缀的分数 总和

**注意:**字符串视作它自身的一个前缀。

示例

输入:words = ["abc","ab","bc","b"]
输出:[5,4,3,2]
解释:对应每个字符串的答案如下:
- "abc"3 个前缀:"a""ab""abc" 。
- 2 个字符串的前缀为 "a"2 个字符串的前缀为 "ab"1 个字符串的前缀为 "abc" 。
总计 answer[0] = 2 + 2 + 1 = 5 。
- "ab"2 个前缀:"a""ab" 。
- 2 个字符串的前缀为 "a"2 个字符串的前缀为 "ab" 。
总计 answer[1] = 2 + 2 = 4 。
- "bc"2 个前缀:"b""bc" 。
- 2 个字符串的前缀为 "b"1 个字符串的前缀为 "bc" 。 
总计 answer[2] = 2 + 1 = 3 。
- "b"1 个前缀:"b"。
- 2 个字符串的前缀为 "b" 。
总计 answer[3] = 2

思路

这次的第四题比较简单,直接用一个裸Trie树就行了。在Trie树上的每一个节点额外维护一下计数信息cnt,表示有多少个单词经过了这个节点。求解一个单词时,对这个单词所有字符节点的cnt进行一下累加即可。

class Solution {
    // Trie树上的节点
    class Node {
        int val;
        int cnt;
        boolean end; // end标记其实没用, 可以去掉

        Node[] sons = new Node[26];

        Node(int val) {
            this.val = val;
            this.cnt = 1;
            this.end = false;
        }
    }
	
    // Trie 树根节点
    Node root = new Node(-1);

    private void add(String s) {
        int n = s.length();
        Node cur = root;
        for (int i = 0; i < n; i++) {
            int u = s.charAt(i) - 'a';
            if (cur.sons[u] == null) cur.sons[u] = new Node(u);
            else cur.sons[u].cnt++;
            cur = cur.sons[u];
        }
        cur.end = true;
    }

    private int find(String s) {
        Node cur = root;
        int n = s.length();
        int ret = 0;
        for (int i = 0; i < n; i++) {
            int u = s.charAt(i) - 'a';
            cur = cur.sons[u];
            ret += cur.cnt;
        }
        return ret;
    }

    public int[] sumPrefixScores(String[] words) {
        for (String s : words) add(s);
        int[] ans = new int[words.length];
        for (int i = 0; i < words.length; i++) {
            ans[i] = find(words[i]);
        }
        return ans;
    }
}

总结

这周日早上被对象拉着去打羽毛球,可我大抵是病了,心里横竖都想着周赛,这冲动没有来由。

于是背了台轻薄本就出门了。10点半刚好到球场,然后就在球场边的板凳上打起了周赛😂

好在这次周赛难度不大,差不多11点10分的时候做完了4道题,然后就开心的打球去啦!

事后想想,还好我背上了电脑参加了周赛,不然就要错过好不容易的AK的机会!😭