leetcode514. 自由之路/记忆化dfs

题目: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 的下一个字符(下一阶段), 直至完成所有拼写。
示例:

在这里插入图片描述

输入: ring = "godding", key = "gd"
输出: 4
解释:
 对于 key 的第一个字符 'g',已经在正确的位置, 我们只需要1步来拼写这个字符。 
 对于 key 的第二个字符 'd',我们需要逆时针旋转 ring "godding" 2步使它变成 "ddinggo"。
 当然, 我们还需要1步进行拼写。
 因此最终的输出是 4

提示:

  • ring 和 key 的字符串长度取值范围均为 1 至 100;
  • 两个字符串中都只有小写字符,并且均可能存在重复字符;
  • 字符串 key 一定可以由字符串 ring 旋转拼出。

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

基本思想:记忆化dfs

  • 这道题的思想很直观,考虑每一种情况,找最终结果最小的值,当前情况下有两种选择顺时针旋转和逆时针旋转
  • 如果直接进行dfs不进行任何的优化,结果会超时,这里参考如下链接:点击链接,进行如下两方面的优化:
    • 将ring中的每一个字符保存在map中,每次查找字符的时候不用进行遍历了
    • dfs的过程中保存中间结果,避免出现重复的递归,因为子问题存在重复的现象(这里其实不太好想,可以模拟下递归过程来理解下)

cpp版本

class Solution {
public:
    int findRotateSteps(string ring, string key) {
        /*
        利用dfs,每一次转化成key的时候,其实是有两种选择,考虑每一种选择,最后找到最小步数;
        在此过程中要保存已经旋转后的字符串
        */
        map<char, vector<int>> mp;//保存ring中每个字符对应的位置
        map<pair<int, int>, int> memo;//保存下key中字符和ring中的对应位置,避免重复搜索
        for(int i = 0; i < ring.size(); ++i){
            mp[ring[i]].push_back(i);
        }        
        return dfs(memo, ring, mp, 0, key, 0) + key.size(); //这里加上的按下按钮的次数
    }
    int dfs(map<pair<int, int>, int> &memo, string ring, map<char, vector<int>>& mp, int ring_pos, string key, int key_pos){
        int res = INT_MAX;
        if(key_pos == key.size()){
            return 0;
        }
        if(memo.find({key_pos, ring_pos}) != memo.end()){
            return memo[{key_pos, ring_pos}];
        }
        for(auto pos : mp[key[key_pos]]){
            // int t1 = (pos - ring_pos + ring.size()) % ring.size();
            // int t2 = (ring_pos - pos + ring.size()) % ring.size();
            int t1 = abs(pos - ring_pos);
            int t2 = ring.size() - t1;
            res = min(res, min(t1, t2) + dfs(memo, ring, mp, pos, key, key_pos + 1));
        }
        memo[{key_pos, ring_pos}] = res;
        return res;

    }
};

python版本

  • 这里要特别注意:当用list表示二维数组时,因为在内部函数调用的过程中会对该数组进行更改,所以在初始定义的时候要注意浅拷贝问题
    memo = [[-1] * len(key)] * len(ring) 这种定义形式是指len(ring)个[-1] * len(key)这个数组的引用,因此修改任何一个改变的都是这一列

参考链接:点击连接

class Solution:
    def findRotateSteps(self, ring: str, key: str) -> int:
        ## 记忆化递归这里有问题!!!!!
        mp = defaultdict(list) # 保存ring中每个元素的位置
        # memo = [[-1] * len(key)] * len(ring) 出现浅拷贝问题!!!
        memo = []
        for i in range(len(ring)):
            memo.append([])
            for j in range(len(key)):
                memo[i].append(-1)
        i = 0
        for r in ring:
            mp[r].append(i)
            i += 1
        # for k, v in mp.items():
        #     print(v)
        def dfs(ring_pos: int, key_pos: int) -> int:
            # print(ring_pos, key_pos)
            nonlocal memo
            if key_pos == len(key):
                return 0
            if memo[ring_pos][key_pos] != -1:
                return memo[ring_pos][key_pos]
            
            res = sys.maxsize
            for pos in mp[key[key_pos]]:
                t1 = (pos - ring_pos + len(ring)) % len(ring)
                t2 = (ring_pos - pos + len(ring)) % len(ring)
                res = min(res, min(t1, t2) + dfs(pos, key_pos + 1))
            
            memo[ring_pos][key_pos] = res

            return res
        cc = dfs(0, 0)
        for m in memo:
            print(m)
        
        return len(key) + cc
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值