Leetcode 自由之路



前言

https://leetcode-cn.com/problems/freedom-trail/
题目见链接,这里不再赘述


一、解法的关键点

示例:这道题目是一道典型的动态规划题目,这个问题里dp函数是一个二维的,以key的第i个位置的值作为索引,去推导dp[i][j]。以ring=“godding” key="gd"为例。以d为例,在ring中,有两个d在不同的位置出现,因此,在dp[i-1][jg]确定的情况下,我们可以求得从不同的g出发,转到两个d的位置,即从index :0->2,0->3,6->2,6->3这四个选项,取其中最小的值即为最近的路径,在加上每次按下button的一步,即为最后的结果。
为了快速的确定key中第i个字符在ring中的备选位置,因此要维护一个post[]数组存储备选位置。转移方程为,其中i为key中第i个字符,j为ring中的选项,k为key中第i-1个字符所对应的在ring中的位置index
dp[i][j]=Math.min(dp[i-1][k]+Math.min(Math.abs(j-k),n-Math.abs(j-k))+1,dp[i][j])

二、使用步骤

1核心代码

for (int i = 1; i < m; ++i) {//key中i
            for (int j : pos[key.charAt(i) - 'a']) {//ring中 j
                for (int k : pos[key.charAt(i - 1) - 'a']) {//dp中j-1
                    dp[i][j] = Math.min(dp[i][j], dp[i - 1][k] + Math.min(Math.abs(j - k), n - Math.abs(j - k)) + 1);
                }
            }
        }

2.完整代码

 int m=key.length();
        int n=ring.length();
        List<Integer>[] pos = new List[26];
        for (int i = 0; i < 26; ++i) {
            pos[i] = new ArrayList<Integer>();
        }
        for (int i = 0; i < n; ++i) {
            pos[ring.charAt(i) - 'a'].add(i);
        }
        int[][] dp=new int[m][n];
        for (int i = 0; i < m; ++i) {
            Arrays.fill(dp[i], 0x3f3f3f);
        }
        for (int x: pos[key.charAt(0)-'a']) {
            dp[0][x]=Math.min(x,n-x)+1;
        }
        for (int i=1;i<m;++i){
            for (int k:pos[key.charAt(i)-'a']){
               for (int ki:pos[key.charAt(i-1)-'a']){
                   dp[i][k]=Math.min(dp[i-1][ki]+Math.min(Math.abs(k-ki),n-Math.abs(k-ki))+1,dp[i][k]);
               }
            }
        }
        return  Arrays.stream(dp[m - 1]).min().getAsInt();

总结

这道题目的难度是hard,但是是一到相当典型的动态规划题目,思路比较清晰,但是代码的实现存在一些难点。

1.如何清楚明确的维护一个位置表post,最开始的编程中,我使用了Map<Charactar,Integer> 的方式,但是在初始化的过程中存在很大的问题。而且调用很麻烦。现在的这种方式是leetcode官方解答的方式,比较简单。
2.三层循环的代码写的时候要谨慎,避免出现错误。
3.最后的结果为min(dp[m-1][]),即从最后的被选中选出最小值。

动态规划问题,核心终究是枚举的问题,如何更好地枚举需要我们不断学习。这个问题里,dp[i][j]只与dp[i-1][j]有关,因此dp可以简化为一维,但是相应的实现难度也会相应提高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值