LeetCode 5600. 第 K 条最小指令

LeetCode 5600题第K条最小指令解法
博客围绕LeetCode 5600. 第K条最小指令展开。问题是在包含v个‘V’和h个‘H’的排列组合中,找字典序为k的字符串。提出二分法思路,但多次用除法会超时,需直接确定边界,通过计算组合数实现,还可记录组合数以提高效率,最后分析了复杂度。

LeetCode 5600. 第 K 条最小指令

1、问题描述
Bob 站在单元格 (0, 0) ,想要前往目的地 destination :(row, column) 。他只能向 右 或向 下 走。你可以为 Bob 提供导航 指令 来帮助他到达目的地 destination 。

指令 用字符串表示,其中每个字符:

'H' ,意味着水平向右移动
'V' ,意味着竖直向下移动
能够为 Bob 导航到目的地 destination 的指令可以有多种,例如,如果目的地 destination 是 (2, 3)"HHHVV""HVHVH" 都是有效 指令 。

然而,Bob 很挑剔。因为他的幸运数字是 k,他想要遵循 按字典序排列后的第 k 条最小指令 的导航前往目的地 destination 。k  的编号 从 1 开始 。

给你一个整数数组 destination 和一个整数 k ,请你返回可以为 Bob 提供前往目的地 destination 导航的 按字典序排列后的第 k 条最小指令 。


示例 1:

输入:destination = [2,3], k = 1
输出:"HHHVV"
解释:能前往 (2, 3) 的所有导航指令 按字典序排列后 如下所示:
["HHHVV", "HHVHV", "HHVVH", "HVHHV", "HVHVH", "HVVHH", "VHHHV", "VHHVH", "VHVHH", "VVHHH"].
示例 2:

输入:destination = [2,3], k = 2
输出:"HHVHV"
示例 3:


输入:destination = [2,3], k = 3
输出:"HHVVH"
2、思路与方法

问题描述地比较复杂,其实比较容易发现我们从起点到终点共需要往下走d[0]次,我们记为v,往右走d[1]次,记为h,即字符串会包括v个’V’以及h个’H’,在他们的排列组合中,找到字典序为k的字符串。这里对我来说比较好想到的方法应该是二分法,可以看下图:以v=2,h=2,k=4为例

在这里插入图片描述

共有6种情况,我们需要的结果是VHHV,怎么得到呢?可以看出字符串的字典序是按照一定比例关系排列的,例如第一步,h:v = 1:1,那么前三个以H开头,后三个以V开头,因为h和v的数量一致,包含h的那一半必然在前面,我们记录好[1,6],边界线为4,此时需要取出的k为4,显然第一个字符就为’V’。第二步也类似,现在h:v = 2:1,也就是说前2/3为h,后1/3为v,同样记录好[4,6],边界线为[6],此时取4则取’H’,后面几步也类似,根据这个比例关系则可以一步步得到最终结果:VHHV。

这是一个比较容易理解的思路,但是在本题中多次使用除法会导致超时(我猜),因此我们不能用除法来确定边界,需要直接获得。考虑第一步,记录为[1,6],怎么确定边界4?其实可以发现,只要求出取出H之后,字符串的组合数,便可以找到边界,我们取出一个H后,还剩一个H和两个V,因此组合数为C(3,1) = 3,由此可知边界为 1+3=4。

计算组合数花费的时间更多,但是其实组合数的计算是一个递归的过程(搜的),因此可以在计算C(4,2)的时候,用数据记录其所有包含的组合数,需要的时候取出即可。

代码:
class Solution {
    int[][] dp = new int[40][40];//记录组合数
    
    public String kthSmallestPath(int[] destination, int k) {
        StringBuilder res = new StringBuilder("");
        int v = destination[0];
        int h = destination[1];
        int left = 1;
        int right = f((v+h),h);//二分法的left和right指针
        for(int i = 0;i<destination[0]+destination[1];++i) {
            if(h == 0) {
                res.append('V');
                --v;
                continue;
            }
            if(v == 0) {
                res.append('H');
                --h;
                continue;
            }
            
            int pos = left+dp[h+v-1][h-1];
            
            if(k < pos) {
                res.append('H');
                right = pos-1;
                --h;
            } else {
                res.append('V');
                left = pos;
                --v;
            }
        }
        return res.toString();
    }

    private int f(int n, int k) {
		if(k==0||k==n) {
            dp[n][k] = 1;
            return 1;
        }
        if(dp[n][k] == 0) 
            dp[n][k] = f(n-1, k)+f(n-1, k-1);
        return dp[n][k];
        
    }
}
4、复杂度

时间复杂度:算法的效率还行,3ms,不包括组合数的计算的话复杂度为O(n)

空间复杂度:需要用一个二维数组记录,不过本题范围很小,可以看作常数量级或者O(n2)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值