1. 题目来源
链接:lc514. 自由之路
2. 题目说明
3. 题目解析
其实这个是很经典的线性 dp
变种题。状态定义、状态转移都是很自然的。
思路:
- 状态定义:
f[i][j]
已经输出了key
的前i
个字符,且输出第key[i]
时,指针位于ring[j]
的所有方案步骤的最小值
- 状态转移:
- 集合划分一般找最后一步不同点。即,考虑
j-1
步指针是位于什么位置转移到j
的。显然转盘的n
中可能位置都是备选位置,都会发生状态转移。假设,输出key[i-1]
,时指针位于k
位置,此时意义为已经输出了key
的前i-1
个字符,且输出第key[i-1]
时指针位于ring[j-1]
的所有方案步骤的最小值,其等价于f[i-1][k]
。k
可取1~n
,最后从k
位置统一转到key[i]
所在位置。在此考虑下顺、逆时针即可
- 集合划分一般找最后一步不同点。即,考虑
- 状态转移方程:
n
为环形字符串长度,m
为key
的长度- 如果
key[i] == ring[j]
则从上一个状态进行转移f[i][j]=min(f[i][j],f[i-1][k]+min(abs(k-j), abs(n-k+j)) + 1)
- 边界情况及初始化:
- 取
min
,f
数组置为正无穷 - 初始状态输出
key
的第一个字符之前,假定指针位于key
的第 0 个字符。这相当于指针输出了key
的第 0 个字符,且指针位于ring
的第一个字母,该点由题目说明,初始时指针即位于ring
的第一个字母。即f[0][1] = 0
,不做任何操作 - 答案即为:
min(f[m][i]) i 为转盘各个位置
- 取
代码:
class Solution {
public:
int findRotateSteps(string ring, string key) {
int n = ring.size(), m = key.size();
ring = ' ' + ring, key = ' ' + key;
vector<vector<int>> f(m + 1, vector<int>(n + 1, 1e8));
f[0][1] = 0;
for (int i = 1; i <= m; ++i) // 枚举key大小
for (int j = 1; j <= n; ++j) // 枚举输出i时j指针的位置
if (key[i] == ring[j]) { // 当前key字符和指针指向字符相等才可进行状态转移
for (int k = 1; k <= n; ++k) { // 枚举i-1个字符输出时,指针的所有位置,所有状态取min
int t = abs(k - j); // 由于是环形,求距离加上abs,另外一个方向长度就是n-t
f[i][j] = min(f[i][j], f[i - 1][k] + min(t, n - t) + 1); // 走到j位置还要按下去
}
}
int res = 1e8;
for (int i = 1; i <= n; ++i) res = min(res, f[m][i]); // 在各个位置下输出所有的k的最小值
return res;
}
};