LeetCode514自由之路

定义状态

我们用dp[i][j] 表示从游戏开始到拼接完成key[0:i] 在ring[j]==key[i]的时候最小步数
可能很多人对后半句话很困惑,什么是:在ring[j]==key[i]的时候最小步数
这是本题的难点所在,因为存在重复字符串,所以我们把所有重复的位置都考虑到。
比如一下情况:
ring="ababac"
key="ab"
第一步 我们先开始拼接a,我们要把ring中每一个a都计算到
计算的结果是:
dp[0][0]=1
dp[0][2]=3
dp[0][4]=3
为什么出现上面三个结果,因为ring中总共有三个a,比如dp[0][4]的第二维度4表示a在ring中的下标。
还是那句话:因为存在重复字符串,所以我们把所有重复的位置都考虑到。
第二步 我们开始算b,上一步指针停留的位置必然是某个a的位置,我们无法知道哪个a的位置更优,所以都考虑到
也就是说我们对于上个每一个a得位置和现在每一个b得位置 都要计算。
比如对于在位置1的b,我们要算dp[1][1]=min(dp[0][0]+m+1,dp[0][2]+m+1,dp[0][4]+m+1)
其中m是两个位置的距离,
比如对于在位置2的b,我们要算dp[1][3]=min(dp[0][0]+m+1,dp[0][2]+m+1,dp[0][4]+m+1)
下图是上述的所以搜索路径

这道题难就难在ring字符串中字符可重复,所以需要dp数组记录所有相同字符。

class Solution {
    Map<Character,List<Integer>> mp;
    int[][] dp;
    public int findRotateSteps(String ring, String key) {
        int rl=ring.length();
        int kl=key.length();
        if(kl==0) return 0;
        char[] r=ring.toCharArray();
        char[] k=key.toCharArray();
        mp = new HashMap<>();
        for(int i=0;i<rl;i++){//保存字符出现的位置
            if(mp.containsKey(r[i])){
                mp.get(r[i]).add(i);
            }
            else{
                List<Integer> next= new ArrayList<>();
                next.add(i);
                mp.put(r[i],next);
            }
        }
        dp = new int[kl][rl];
        List<Integer> next2 = mp.get(k[0]);
        Iterator<Integer> it2 = next2.iterator();
        while(it2.hasNext()){
            int c = it2.next();
            int m = Math.min(c,rl-c);//找到每个位置
            dp[0][c]=m+1;
        }
        for(int i=1;i<kl;i++){

            List<Integer> next = mp.get(k[i]);
            Iterator<Integer> it = next.iterator();
            while(it.hasNext()){
                int c = it.next();//找到本次的所有位置
                int min=Integer.MAX_VALUE;
                List<Integer> next1 = mp.get(k[i-1]);
                Iterator<Integer> it1 = next1.iterator();
                while(it1.hasNext()){
                    int d = it1.next();//找到上个字符所有的位置来计算
                    //计算上一个k[i-1]到k[i]的最短距离
                    int n = Math.abs(c-d);
                    int m = Math.min(rl-n,n);
                    min=Math.min(min,dp[i-1][d]+m+1);
                }
                dp[i][c]=min;
            }
        }
        int ans=Integer.MAX_VALUE;
        List<Integer> next = mp.get(k[kl-1]);
        Iterator<Integer> it = next.iterator();
        while(it.hasNext()){
            ans=Math.min(ans,dp[kl-1][it.next()]);
        }
        return ans;
    }
}

一种比较好理解的代码格式,思路相同。

class Solution {
    public int findRotateSteps(String ring, String key) {
        int n = ring.length(), m = key.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 i : pos[key.charAt(0) - 'a']) {
            dp[0][i] = Math.min(i, n - i) + 1;
        }
        for (int i = 1; i < m; ++i) {
            for (int j : pos[key.charAt(i) - 'a']) {
                for (int k : pos[key.charAt(i - 1) - 'a']) {
                    dp[i][j] = Math.min(dp[i][j], dp[i - 1][k] + Math.min(Math.abs(j - k), n - Math.abs(j - k)) + 1);
                }
            }
        }
        return Arrays.stream(dp[m - 1]).min().getAsInt();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值