LeetCode 514: Freedom Trail 解题与思考

LeetCode 514: Freedom Trail 解题与思考

[原题链接]

题目描述

我们有一个轮盘,轮盘上有一个首尾相接的字符串S,我们有如下操作
1、转轮盘到下一个或者上一个字符,需要一步操作
2、输出当前字符,需要一步操作
假如我们有目标字符串K,求取出K的最小操作步数

轮盘

思路

第一感觉就是用DP,因为看上去除去末尾的“上一个字符串”所需要的距离,加上从上一个最小距离,然后求极小值,就是所需答案。
但是遇到了几个问题:

  • 假设结束的字符为E,在轮盘中有若干个相同的E,你不知道以哪个结尾是最优解
  • 你也不知道上一个字符串的末尾停留在哪个位置时能取到最优解

所以我们至少要把问题稍微做一下转换:

Step1
假设字符串 Kn 长度为n,以 En 结尾,有p个E( En,1,En,2,....,En,p )(此处每个变量分别记录其所在位置),那么,我们就是在寻找以某个位于 En,i 结尾所得到的字符串K所用步数最少。
记字符串 Ei 结尾所需要的步数为 Sn(En,i) ,记得到长度为n的字符串K的最小步数为 Kn

K=min(Sn(En,i))

Step2
然后这个时候我们需要求解 Sn(En,i) ,问题就变成:求解以某个特定位置 En,i 结尾的得到K的最小步数。这个问题就是很明显的DP了。
不妨设上一个字符串为 Kn1 ,结尾字符为 En1 , 假设我们已经求得 Sn1(En1,j) ,也即以位置 En1,j 结尾的时候所需的最小步数,那么我们只要找出从所有 En1 中出发到达 En,i 中所用步数最少的,就是以特定位置 En,i 结尾的最小步数,即

Sn(En,i)=min(Sn1(En1,j)+En1,jEn,i)

于是递推式就出来了。其中 Sk 则为最优子问题。

算法

设计一个栈,栈内元素为二元对,记录了某个字符c的位置以及到达c耗费的最小步数

假设有两个这样的栈,一个为now,一个为last,分别记录当前结尾字符所在位置以及最小步数,和上一个结尾字符所在位置以及最小步数

初始化:我们将轮盘开始字符压now栈,最小步数为0;
以n记录取到字符串的第几个

循环做以下步骤:
1、将now的元素移动到last
2、对于字符串 Kn ,假设结尾字符为 En ,对每个 En,i ,从last栈中取出所有的元素,按照②式与之计算最短路径,求最小值,然后压入now
3、n自加1

最后的解为now栈中所有最小路径中的最小值

代码

#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Solution {
    int distance(int a, int b, int size) {
        return (a < b) ? ((b - a > size / 2) ? (size + a - b) : (b - a)) : ((a - b > size / 2) ? (size + b - a) : (a - b));
    }
public:
    int findRotateSteps(string ring, string key) {
        vector<vector<int>> charArr;
        vector<pair<int, int>> sol, solNext;
        sol.push_back(pair<int, int>(0, 0));
        int nowChar = 0;
        charArr.resize(26);
        int i = 0;
        for ( auto c : ring ) {
            charArr.at(c - 'a').push_back(i++);
        }
        while ( nowChar != key.size() ) {
            for ( auto pos : charArr.at(key.at(nowChar) - 'a') ) {
                pair<int, int> best(pos, 100000);
                for ( auto nowpos : sol ) {
                    int dis = distance(nowpos.first, pos, ring.size());
                    if ( nowpos.second + dis + 1 < best.second ) {
                        best.second = nowpos.second + dis + 1;
                    }
                }
                solNext.push_back(best);
            }
            sol = solNext;
            solNext.resize(0);
            nowChar++;
        }
        int answer = 100000;
        for ( auto ans : sol ) {
            answer = (ans.second < answer) ? ans.second : answer;
        }
        return answer;
    }
};

思考

原题目是不怎么好DP的,需要绕个弯子,总的来说不难

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值