每日一题补题记录II

2045. 到达目的地的第二短时间

城市用一个 双向连通 图表示,图中有 n 个节点,从 1 到 n 编号(包含 1 和 n)。图中的边用一个二维整数数组 edges 表示,其中每个 edges[i] = [ui, vi] 表示一条节点 ui 和节点 vi 之间的双向连通边。每组节点对由 最多一条 边连通,顶点不存在连接到自身的边。穿过任意一条边的时间是 time 分钟。

每个节点都有一个交通信号灯,每 change 分钟改变一次,从绿色变成红色,再由红色变成绿色,循环往复。所有信号灯都 同时 改变。你可以在 任何时候 进入某个节点,但是 只能 在节点 信号灯是绿色时 才能离开。如果信号灯是  绿色 ,你 不能 在节点等待,必须离开。

第二小的值 是 严格大于 最小值的所有值中最小的值。

例如,[2, 3, 4] 中第二小的值是 3 ,而 [2, 2, 4] 中第二小的值是 4 。
给你 n、edges、time 和 change ,返回从节点 1 到节点 n 需要的 第二短时间 。

注意:

你可以 任意次 穿过任意顶点,包括 1 和 n 。
你可以假设在 启程时 ,所有信号灯刚刚变成 绿色 。

对于这个题目,我们可以用这样一种方式思考,肯定是使用BFS了,但是这样我们只能找到用时最短的时间,为了找到用时第二短的时间,我们可以使用visited数组来储存是否访问过,如果第一次访问到n,就将其visited设置为真,第二次访问到时,必然是用时第二短的时间,同时还要考虑,必须和最短时间严格不等!

class Solution {
    public int secondMinimum(int n, int[][] edges, int time, int change) {
        // 每条边的权重是一样的,所以,我们只需要找到次短路就可以了
        // 一样使用BFS,一层一层的遍历,第二次遇到 n 才结束

        // 先构造 n 个节点,将边整合到点中
        HashMap<Integer,List<Integer>>graph=new HashMap<>();
        int[]visited=new int[n+1];

        Node[] nodes = new Node[n + 1];
        for (int i = 1; i <= n; i++) {
            nodes[i] = new Node();
        }

        // 整理所有边,边中每个点的邻接表内都加入另一个点
        for (int[] edge : edges) {
            nodes[edge[0]].neighbours.add(nodes[edge[1]]);
            nodes[edge[1]].neighbours.add(nodes[edge[0]]);
        }
        Deque<Node> queue = new LinkedList<>();

        // 先把第 1 个节点入队,并设置其访问次数为 1
        queue.offer(nodes[1]);
        nodes[1].visited++;

        int ans = 0;
        // 遍历的层级,与二叉树的层序遍历类似
        int level = 0;
        while (!queue.isEmpty()) {
            // 层级加一
            level++;
            // 计算时间
            if (ans % (2 * change) >= change) {
                ans += 2 * change - ans % (2 * change);
            }
            ans += time;
            // 一层一层的遍历
            int size = queue.size();
            while (size> 0) {
                size--;
                // 弹出当前层的节点
                Node node = queue.poll();
                // 遍历其下级的节点
                for (Node next : node.neighbours) {
                    // 如果下级节点有等于 n 的,并且不是初始状态,并且不等于当前层级
                    // 说明之前已经遍历过一次了,那就直接返回吧
                    if (next == nodes[n] && next.level != 0 && next.level != level) {
                        return ans;
                    }
                    // 剪枝1:同一个层级同一个节点出现多次,只需要入队一次
                    // 剪枝2:同一个节点被访问了两次及以上(同一层级多次算一次),它肯定不可能是次短路径的一部分
                    if (next.level != level && next.visited < 2) {
                        queue.offer(next);
                        next.level = level;
                        next.visited++;
                    }
                }
            }
        }
        return -1;
    }

    class Node {
        /**
         * 记录从起点到该点的层级
         */
        int level = 0;
        /**
         * 记录该点相连的节点
         */
        List<Node> neighbours = new ArrayList<>();
        /**
         * 记录被访问过几次,同一个节点在同一层级被访问多次算一次
         */
        int visited = 0;
    }
}

2047. 句子中的有效单词数

句子仅由小写字母('a' 到 'z')、数字('0' 到 '9')、连字符('-')、标点符号('!'、'.' 和 ',')以及空格(' ')组成。每个句子可以根据空格分解成 一个或者多个 token ,这些 token 之间由一个或者多个空格 ' ' 分隔。

如果一个 token 同时满足下述条件,则认为这个 token 是一个有效单词:

仅由小写字母、连字符和/或标点(不含数字)。
至多一个 连字符 '-' 。如果存在,连字符两侧应当都存在小写字母("a-b" 是一个有效单词,但 "-ab" 和 "ab-" 不是有效单词)。
至多一个 标点符号。如果存在,标点符号应当位于 token 的 末尾 。
这里给出几个有效单词的例子:"a-b."、"afad"、"ba-c"、"a!" 和 "!" 。

给你一个字符串 sentence ,请你找出并返回 sentence 中 有效单词的数目 。

先根据空格分割字符串为字符串数组ss,考虑到可能用多个空格分隔,如果某个子串中含有空格,即证明这个子串不是合法单词,取消。

我们会遍历ss数组,然后判断每个字符串是不是合法单词,如果是则ans+1,最后返回ans。

判断方式使用正则表达式,考虑到不同长度的字符串,如果长度为1,可以为单独的符号或字母,大于1时,符号只能在末尾

class Solution {
    public int countValidWords(String sentence) {
        String[]ss=sentence.trim().split("[ ]+");
        int ans = 0;
        for (String s : ss) if (checkValidWords(s)) ans++;
        return ans;
    }
    private boolean checkValidWords(String s) {
        //长度大于一
        if (s.length() > 1) 
            return s.matches("^[a-z]+(-[a-z]+)?[,.!]?$");
        //长度为1,可以为单独的符号,或者单独的字母
        else return s.matches("[!|,|.|[a-z]]");
    }
}

上一篇:我对Android Framework的学习经验总结


下一篇:PAT (Basic Level) Practice 1057 数零壹 (20 分)