514. 自由之路

简介: 514. 自由之路

514. 自由之路

电子游戏“辐射4”中,任务 “通向自由” 要求玩家到达名为 “Freedom Trail Ring” 的金属表盘,并使用表盘拼写特定关键词才能开门。

给定一个字符串 ring ,表示刻在外环上的编码;给定另一个字符串 key ,表示需要拼写的关键词。您需要算出能够拼写关键词中所有字符的最少步数。

最初,ring 的第一个字符与 12:00 方向对齐。您需要顺时针或逆时针旋转 ring 以使 key 的一个字符在 12:00 方向对齐,然后按下中心按钮,以此逐个拼写完 key 中的所有字符。

旋转 ring 拼出 key 字符 key[i] 的阶段中:

您可以将 ring 顺时针或逆时针旋转 一个位置 ,计为1步。旋转的最终目的是将字符串 ring 的一个字符与 12:00 方向对齐,并且这个字符必须等于字符 key[i] 。
如果字符 key[i] 已经对齐到12:00方向,您需要按下中心按钮进行拼写,这也将算作 1 步。按完之后,您可以开始拼写 key 的下一个字符(下一阶段), 直至完成所有拼写。

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

一、暴力递归

首先我们要引入一个函数minDial()
这个函数解决环内i1下标与i2下标顺时针或逆时针的最短距离

    //n为转盘的长度
    public int minDial(int i,int j,int n){
        return Math.min(Math.abs(i-j),Math.min(i,j)+n-Math.max(i,j));
    }

process(0, 0, aim, map, N)
递归含义:从上一个拨号位置,到下一个要拨号的字母,最小的代价是什么
我们需要用一个哈希表记录每个字母出现的位置
在递归中,遍历下一个字母每个位置,取最小的代价

    public static int findRotateSteps(String r, String k) {
        char[] ring = r.toCharArray();
        int N = ring.length;
        HashMap<Character, ArrayList<Integer>> map = new HashMap<>();
        for (int i = 0; i < N; i++) {
            if (!map.containsKey(ring[i])) {
                map.put(ring[i], new ArrayList<>());
            }
            map.get(ring[i]).add(i);
        }
        char[] str = k.toCharArray();
        int M = str.length;
        return process(0, 0, str, map, N);
    }

    // 电话里:指针指着的上一个按键preButton
    // 目标里:此时要搞定哪个字符?keyIndex
    // map : key 一种字符 value: 哪些位置拥有这个字符
    // N: 电话大小
    // f(0, 0, aim, map, N)
    public static int process(int preButton, int index, char[] str, HashMap<Character, ArrayList<Integer>> map, int N,int[][] dp) {
        if (index == str.length) {
            return 0;
        } 
        int ans = Integer.MAX_VALUE;
        // 还有字符需要搞定
        char cur = str[index];
        ArrayList<Integer> nextPositions = map.get(cur);
        for (int next : nextPositions) {
            int cost = dial(preButton, next, N) + 1 + process(next, index + 1, str, map, N);
            ans = Math.min(ans, cost);
        }
        return ans;
    }
    public static int dial(int i1, int i2, int size) {
        return Math.min(Math.abs(i1 - i2), Math.min(i1, i2) + size - Math.max(i1, i2));
    }

二、记忆化搜索

我们使用dp来记录每个位置出现的答案,如果出现过,直接返回就行了,初始化全为-1,代表没有出现过

    public int N;
    public int findRotateSteps(String ring, String key) {
        HashMap<Character,ArrayList<Integer>> map=new HashMap<>();
        char[] r=ring.toCharArray();
        N=r.length;
        for(int i=0;i<N;i++){
            if(!map.containsKey(r[i])){
                map.put(r[i],new ArrayList<Integer>());
            }
            map.get(r[i]).add(i);
        }
        char[] aim=key.toCharArray();
        int m=aim.length;
        int[][] dp=new int[N][m+1];
        for(int i=0;i<N;i++){
            for(int j=0;j<=m;j++){
                dp[i][j]=-1;
            }
        }
        return process(0,0,aim,map,dp);
    }
    public int process(int pre,int index,char[] aim,HashMap<Character,ArrayList<Integer>> map,int[][] dp){
        if(dp[pre][index]!=-1){
            return dp[pre][index];
        }
        int ans=Integer.MAX_VALUE;
        if(index==aim.length){
            ans=0;
        }else{
            ArrayList<Integer> nextPosition=map.get(aim[index]);
            for(int next:nextPosition){
                ans=Math.min(ans,minDial(pre,next,N)+1+process(next,index+1,aim,map,dp));
            }
        }
        dp[pre][index]=ans;
        return  ans;
    }

    public int minDial(int i,int j,int n){
        return Math.min(Math.abs(i-j),Math.min(i,j)+n-Math.max(i,j));
    }

动态规划

我们直接根据暴力递归改动态规划
主函数调用递归的参数,我们返回dp0;
因为每次调用递归都是index+1,因此dp要反着填
我们从右往左填
dpi代表从i出发到aim[j]的最小代价
我们可以从下到上填好dpi

    public int minDial(int i,int j,int n){
        return Math.min(Math.abs(i-j),Math.min(i,j)+n-Math.max(i,j));
    }

    public int findRotateSteps(String ring, String key) {
        HashMap<Character,ArrayList<Integer>> map=new HashMap<>();
        char[] r=ring.toCharArray();
        int n=r.length;
        for(int i=0;i<n;i++){
            if(!map.containsKey(r[i])){
                map.put(r[i],new ArrayList<Integer>());
            }
            map.get(r[i]).add(i);
        }
        char[] aim=key.toCharArray();
        int m=aim.length;
        int[][] dp=new int[n+1][m+1];
        for(int j=m-1;j>=0;j--){
            for(int i=n-1;i>=0;i--){
                char c=aim[j];
                int res=Integer.MAX_VALUE;
                for(int next:map.get(c)){
                    res=Math.min(res,dp[next][j+1]+1+minDial(i,next,n));
                }
                dp[i][j]=res;
            }
        }
        return dp[0][0];
    }
相关文章
|
Cloud Native Linux Go
开源哲学:自由、共享与合作
开源哲学:自由、共享与合作
116 0
开源哲学:自由、共享与合作
|
机器学习/深度学习 并行计算 程序员
DragGAN 完全自由 P 图指南
在上篇中,树先生教大家如何正确部署 DragGAN 项目,实现自由拖拽式 P 图。 但可惜只能使用项目预置的一些图片,本篇教大家如何利用该项目自由编辑修改任何图片。
|
域名解析 应用服务中间件 Linux
再一次,实现听歌自由
2002年11月,百度上线MP3搜索功能,几乎能搜索和下载到所有的歌曲。按相关的版权法规,百度未经授权使用他人资源牟利是违法的。当时互联网产业违法采集数据、传播盗版是家常便饭,版权管理形同虚设,百度顺势而为分了一块大蛋糕。盗版音乐砸了音乐人的饭碗,就如同盗版软件摧残软件从业者。最近十多年,政府对音像作品版权的管理日趋严格,这是一件利国利民的好事,一个行业兴盛的起点首先是从业者得到应有的报酬。
367 0
|
监控 数据可视化 数据库
零代码,让业务人员实现应用创造自由
随着低代码技术的发展,逐渐出现了零代码。顾名思义,零代码应用构建过程中不需要使用任何代码。“不需要使用任何代码”,是优势,也是诅咒。一方面,零代码可以最大限度地允许业务人员参与应用构建,赋能全民开发者,从而激发软件开发生产力;另一方面,开发的自由度受平台能力制约,可开发的应用类型和复杂度存在天然的“上限”。
321 0
零代码,让业务人员实现应用创造自由
管理感悟:软件的特性
管理感悟:软件的特性
77 0
|
机器人 测试技术 项目管理
瀑布型项目管理最常用的10个小工具,可以自由搭建使用
瀑布型项目管理最常用的10个小工具,可以自由搭建使用
|
前端开发 程序员 开发者
程序人生:程序员如何实现财富自由?
程序人生:程序员如何实现财富自由?
340 0
程序人生:程序员如何实现财富自由?
|
算法 Linux 程序员
开源是自由的,永远
开源软件到底受不受美国政府管制?会不会应美国政府的要求禁运?最近这个话题成了热点。遗憾的是,到现在中文文章里我没看到能把这个事情说清楚的文章,这让我非常惊讶。中国科技和互联网行业从开源软件中受益极大,也有无数直接和间接参与者,但是这些基本事实还是糊涂的,比较遗憾。
178 0
|
人工智能 前端开发 数据可视化
玉伯:做一个简单自由有爱的技术人
前端工程师如何成长?如何管理前端团队?如何打造团队文化?近日,蚂蚁研究员兼体验技术部负责人玉伯,在蚂蚁内部技术人的成长公开课上,分享了他的人生愿景和心路历程。
玉伯:做一个简单自由有爱的技术人