LeetCode 1629. 按键持续时间最长的键 / 306. 累加数 / 1036. 逃离大迷宫(有范围的bfs、离散化+bfs)

1629. 按键持续时间最长的键

2022.1.9 每日一题

题目描述

LeetCode 设计了一款新式键盘,正在测试其可用性。测试人员将会点击一系列键(总计 n 个),每次一个。

给你一个长度为 n 的字符串 keysPressed ,其中 keysPressed[i] 表示测试序列中第 i 个被按下的键。releaseTimes 是一个升序排列的列表,其中 releaseTimes[i] 表示松开第 i 个键的时间。字符串和数组的 下标都从 0 开始 。第 0 个键在时间为 0 时被按下,接下来每个键都 恰好 在前一个键松开时被按下。

测试人员想要找出按键 持续时间最长 的键。第 i 次按键的持续时间为 releaseTimes[i] - releaseTimes[i - 1] ,第 0 次按键的持续时间为 releaseTimes[0] 。

注意,测试期间,同一个键可以在不同时刻被多次按下,而每次的持续时间都可能不同。

请返回按键 持续时间最长 的键,如果有多个这样的键,则返回 按字母顺序排列最大 的那个键。

示例 1:

输入:releaseTimes = [9,29,49,50], keysPressed = “cbcd”
输出:“c”
解释:按键顺序和持续时间如下:
按下 ‘c’ ,持续时间 9(时间 0 按下,时间 9 松开)
按下 ‘b’ ,持续时间 29 - 9 = 20(松开上一个键的时间 9 按下,时间 29 松开)
按下 ‘c’ ,持续时间 49 - 29 = 20(松开上一个键的时间 29 按下,时间 49 松开)
按下 ‘d’ ,持续时间 50 - 49 = 1(松开上一个键的时间 49 按下,时间 50 松开)
按键持续时间最长的键是 ‘b’ 和 ‘c’(第二次按下时),持续时间都是 20
‘c’ 按字母顺序排列比 ‘b’ 大,所以答案是 ‘c’

示例 2:

输入:releaseTimes = [12,23,36,46,62], keysPressed = “spuda”
输出:“a”
解释:按键顺序和持续时间如下:
按下 ‘s’ ,持续时间 12
按下 ‘p’ ,持续时间 23 - 12 = 11
按下 ‘u’ ,持续时间 36 - 23 = 13
按下 ‘d’ ,持续时间 46 - 36 = 10
按下 ‘a’ ,持续时间 62 - 46 = 16
按键持续时间最长的键是 ‘a’ ,持续时间 16

提示:

releaseTimes.length == n
keysPressed.length == n
2 <= n <= 1000
1 <= releaseTimes[i] <= 109
releaseTimes[i] < releaseTimes[i+1]
keysPressed 仅由小写英文字母组成

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/slowest-key
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

模拟一下就可以了

class Solution {
    public char slowestKey(int[] releaseTimes, String keysPressed) {
        int time = releaseTimes[0];
        char k = keysPressed.charAt(0);
        int l = releaseTimes.length;
        for(int i = 1; i < l; i++){
            int t = releaseTimes[i] - releaseTimes[i - 1];
            if(t > time || (t == time && keysPressed.charAt(i) > k)){
                time = t;
                k = keysPressed.charAt(i);
            }
        }
        return k;
    }
}

306. 累加数

2022.1.10 每日一题

题目描述

累加数 是一个字符串,组成它的数字可以形成累加序列。

一个有效的 累加序列 必须 至少 包含 3 个数。除了最开始的两个数以外,字符串中的其他数都等于它之前两个数相加的和。

给你一个只包含数字 ‘0’-‘9’ 的字符串,编写一个算法来判断给定输入是否是 累加数 。如果是,返回 true ;否则,返回 false 。

说明:累加序列里的数 不会 以 0 开头,所以不会出现 1, 2, 03 或者 1, 02, 3 的情况。

示例 1:

输入:“112358”
输出:true
解释:累加序列为: 1, 1, 2, 3, 5, 8 。1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8

示例 2:

输入:“199100199”
输出:true
解释:累加序列为: 1, 99, 100, 199。1 + 99 = 100, 99 + 100 = 199

提示:

1 <= num.length <= 35
num 仅由数字(0 - 9)组成

进阶:你计划如何处理由过大的整数输入导致的溢出?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/additive-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

考虑大数,所以自定义加法,用字符串表示
然后遍历所有前两个数的情况,判断是否满足条件

class Solution {
    public boolean isAdditiveNumber(String num) {
        //遍历选取前两个数字,然后一直向后判断
        //主要是要处理大数,就手动写一个相加的程序
        int l = num.length();
        int bound = l / 2;
        //遍历长度
        for(int i = 1; i <= bound; i++){
            for(int j = 1; j <= bound; j++){
                String x = num.substring(0, i);
                String y = num.substring(i, i + j);
                if(y.charAt(0) == '0' && y.length() > 1)
                    break;
                if(helper(x, y, l, num, i + j)){
                    return true;
                }
            }
        }
        return false;
    }

    public boolean helper(String x, String y, int l, String num, int idx){
        String xy = add(x, y);
        //System.out.println(xy);
        int len = xy.length();
        int idx2 = idx + len;
        if(idx2 > l)
            return false;
        String temp = num.substring(idx, idx2);
        if(!temp.equals(xy))
            return false;
        if(idx2 == l)
            return true;
        x = y;
        y = temp;
        return helper(x, y, l, num, idx2);
    }

    public String add(String x, String y){
        int lx = x.length();
        int ly = y.length();
        int temp = 0;
        int post = 0;
        StringBuffer sb = new StringBuffer();
        for(int i = lx - 1, j = ly - 1; i >= 0 || j >= 0 || post != 0; i--, j--){
            int a = i >= 0 ? x.charAt(i) - '0' : 0;
            int b = j >= 0 ? y.charAt(j) - '0' : 0;
            int t = a + b + post;
            post = t / 10;
            temp = t % 10;
            sb.append(String.valueOf(temp));
        }
        return sb.reverse().toString();
    }
}

1036. 逃离大迷宫

2022.1.11 每日一题

题目描述

在一个 10^6 x 10^6 的网格中,每个网格上方格的坐标为 (x, y) 。

现在从源方格 source = [sx, sy] 开始出发,意图赶往目标方格 target = [tx, ty] 。数组 blocked 是封锁的方格列表,其中每个 blocked[i] = [xi, yi] 表示坐标为 (xi, yi) 的方格是禁止通行的。

每次移动,都可以走到网格中在四个方向上相邻的方格,只要该方格 不 在给出的封锁列表 blocked 上。同时,不允许走出网格。

只有在可以通过一系列的移动从源方格 source 到达目标方格 target 时才返回 true。否则,返回 false。

示例 1:

输入:blocked = [[0,1],[1,0]], source = [0,0], target = [0,2]
输出:false
解释:
从源方格无法到达目标方格,因为我们无法在网格中移动。
无法向北或者向东移动是因为方格禁止通行。
无法向南或者向西移动是因为不能走出网格。

示例 2:

输入:blocked = [], source = [0,0], target = [999999,999999]
输出:true
解释:
因为没有方格被封锁,所以一定可以到达目标方格。

提示:

0 <= blocked.length <= 200
blocked[i].length == 2
0 <= xi, yi < 10^6
source.length == target.length == 2
0 <= sx, sy, tx, ty < 10^6
source != target
题目数据保证 source 和 target 不在封锁列表内

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/escape-a-large-maze
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

class Solution {
    int n;
    int[][] blocked;
    int[][] dir = {{0,1},{0,-1},{1,0},{-1,0}};
    Set<Pair> set;
    public boolean isEscapePossible(int[][] blocked, int[] source, int[] target) {
        //这个迷宫范围很大,如果暴力肯定会超时
        //那么怎么做呢,看围成的范围吗,在圈内和圈外肯定走不通
        //想到了在障碍点上做文章,因为障碍点相比于迷宫要小很多

        //最后还是不争气的看了答案,这个方法很巧
        //想一件事,就是假设障碍物围成的面积是area,那么如果从一个点出发,
        //bfs经历过的点超过了area,那么说明源点并没有被围住
        //如果s和t两个点都没有被围住,那么说明可以连通
        //那么如果小于area,就一定被围住了吗?需要想通这点
        //如果小于area,肯定是被包围住了,如果包围的范围内能走到t,也是可以的
        //如果走不到,那说明t在包围圈外,是走不通的
        //写一下代码

        n = blocked.length;
        if(n < 2)
            return true;
        this.blocked = blocked;
        set = new HashSet<>();
        for(int[] b : blocked){
            set.add(new Pair(b[0], b[1]));
        }
        int res1 = check(source, target);
        //如果在圈内,那么返回false
        if(res1 == 1)
            return false;
        //如果能走到t,返回true
        else if(res1 == 2)
            return true;
        //否则,看t是否在圈内
        else
            return check(target, source) == 0;  //不在圈内
    }

    public int check(int[] s, int[] t){
        int bound = n * (n - 1) / 2;
        Queue<int[]> queue = new LinkedList<>();
        queue.add(s);
        Set<Pair> vis = new HashSet<>();
        vis.add(new Pair(s[0], s[1]));
        while(!queue.isEmpty() && bound > 0){
            int[] top = queue.poll();
            for(int[] d : dir){
                int nx = top[0] + d[0];
                int ny = top[1] + d[1];
                if(nx < 0 || nx >= 1000000 || ny < 0 || ny >= 1000000)
                    continue;
                if(set.contains(new Pair(nx, ny)) || vis.contains(new Pair(nx, ny)))
                    continue;
                if(nx == t[0] && ny == t[1])
                    return 2;
                bound--;
                queue.add(new int[]{nx, ny});
                vis.add(new Pair(nx, ny));
            }
        }
        //如果被围住了,那么返回1
        if(bound > 0)
            return 1;
        return 0;
    }
}

class Pair{
    int x;
    int y;
    public Pair(int x, int y){
        this.x = x;
        this.y = y;
    }

    public int hashCode(){
        return x * 13131 + y;
    }

    public boolean equals(Object obj){
        if(obj instanceof Pair){
            Pair pair = (Pair)obj;
            return pair.x == x && pair.y == y;
        }
        return false;
    }
}

离散化+bfs

class Solution {
    //学习一下离散化这个思路

    static final int BOUNDARY = 1000000;
    static final int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

    public boolean isEscapePossible(int[][] blocked, int[] source, int[] target) {
        if (blocked.length < 2) {
            return true;
        }
        // 离散化
        TreeSet<Integer> rows = new TreeSet<Integer>();
        TreeSet<Integer> columns = new TreeSet<Integer>();
        //将行列坐标存储到treeset中进行排序
        for (int[] pos : blocked) {
            rows.add(pos[0]);
            columns.add(pos[1]);
        }
        rows.add(source[0]);
        rows.add(target[0]);
        columns.add(source[1]);
        columns.add(target[1]);

        //将set中的数转换成较小的数进行排列,意味着将迷宫地图缩小
        Map<Integer, Integer> rMapping = new HashMap<Integer, Integer>();
        Map<Integer, Integer> cMapping = new HashMap<Integer, Integer>();


        int firstRow = rows.first();
        //如果第0行有值,那么就是0,否则就是1
        int rId = (firstRow == 0 ? 0 : 1);
        rMapping.put(firstRow, rId);
        int prevRow = firstRow;
        for (int row : rows) {
            //如果行相同,不添加
            if (row == firstRow) {
                continue;
            }
            //如果行相邻,那么加1,如果不相邻,加2
            rId += (row == prevRow + 1 ? 1 : 2);
            rMapping.put(row, rId);
            prevRow = row;
        }
        //如果最后一行不是bound-1,那么rid++,表示最后一行
        if (prevRow != BOUNDARY - 1) {
            ++rId;
        }

        //同理处理列
        int firstColumn = columns.first();
        int cId = (firstColumn == 0 ? 0 : 1);
        cMapping.put(firstColumn, cId);
        int prevColumn = firstColumn;
        for (int column : columns) {
            if (column == firstColumn) {
                continue;
            }
            cId += (column == prevColumn + 1 ? 1 : 2);
            cMapping.put(column, cId);
            prevColumn = column;
        }
        if (prevColumn != BOUNDARY - 1) {
            ++cId;
        }

        //然后创建这个迷宫
        int[][] grid = new int[rId + 1][cId + 1];
        //将这个迷宫中对应的点标记为障碍
        for (int[] pos : blocked) {
            int x = pos[0], y = pos[1];
            grid[rMapping.get(x)][cMapping.get(y)] = 1;
        }
        //出发点和终点
        int sx = rMapping.get(source[0]), sy = cMapping.get(source[1]);
        int tx = rMapping.get(target[0]), ty = cMapping.get(target[1]);
        
        //然后在新的迷宫中bfs
        Queue<int[]> queue = new ArrayDeque<int[]>();
        queue.offer(new int[]{sx, sy});
        grid[sx][sy] = 1;
        while (!queue.isEmpty()) {
            int[] arr = queue.poll();
            int x = arr[0], y = arr[1];
            for (int d = 0; d < 4; ++d) {
                int nx = x + dirs[d][0], ny = y + dirs[d][1];
                if (nx >= 0 && nx <= rId && ny >= 0 && ny <= cId && grid[nx][ny] != 1) {
                    if (nx == tx && ny == ty) {
                        return true;
                    }
                    queue.offer(new int[]{nx, ny});
                    //标记已经走过了
                    grid[nx][ny] = 1;
                }
            }
        }
        return false;
    }
}

还有一个只写了思路的思路,看不太懂,不明觉厉。。

### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的学理论和强大的实用效果,动态规划在计算机科学、学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契列问题时,就可以定义一个二维组f[i][j],代表第i项斐波那契列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案。这个问题的解法非常简单,只需要维护一个长度为n的组,记录到达每一级楼梯的方案即可。类似的问题还有“零钱兑换”、“乘积最大子组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与据结构基础》等,来深入理解这种算法思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值