LeetCode1320.最小移动距离的双指键盘输入问题
作者:天晴的故事_665 | 来源:互联网 | 2024-12-21 13:22
本文探讨了在定制键盘上使用两根手指输入字符串时,如何计算最小移动总距离的问题。键盘布局为标准的5行6列,每个大写字母对应一个特定坐标。通过动态规划算法,我们能够有效地找到最优解。
### 问题描述
在一个XY平面上,有一个定制的键盘布局,如图所示。每个大写英文字母位于特定的坐标处,例如字母A位于(0,0),字母B位于(0,1),字母P位于(2,3),字母Z位于(4,1)。
给定一个待输入的字符串`word`,你需要计算并返回在仅使用两根手指的情况下,键入该字符串所需的最小移动总距离。坐标(x1,y1)和(x2,y2)之间的曼哈顿距离是|x1 - x2| + |y1 - y2|。
注意:两根手指的起始位置是零代价的,不计入移动总距离。手指可以任意选择从哪个字母开始。
#### 示例
```cpp
示例 1:
输入: word = "CAKE"
输出: 3
解释:
手指1从C到A的距离为2,手指2从K到E的距离为1,总距离为3。
示例 2:
输入: word = "HAPPY"
输出: 6
解释:
手指1从H到A再到Y的距离为6,手指2从P到P的距离为0。
示例 3:
输入: word = "NEW"
输出: 3
示例 4:
输入: word = "YEAR"
输出: 7
```
#### 提示
- 2 <= word.length <= 300
- 每个字符都是大写英文字母。
### 解题思路
我们使用动态规划来解决这个问题。定义`dp[i][c1][c2]`表示输入完第i个字符后,手指1在字符c1,手指2在字符c2时的最小移动距离。其中,0表示还没有输入字符,1-26表示A-Z。
初始化时,我们将第一个字符分配给手指1,并设置初始状态为0。接下来,我们遍历剩余字符,更新状态转移方程以计算最小移动距离。
#### 代码实现
```cpp
class Solution {
public:
int minimumDistance(string s) {
vector> pos(27, vector(2));
int count = 1;
for (int i = 0; i <5 && count <27; ++i)
for (int j = 0; j <6 && count <27; ++j)
pos[count++] = {i, j}; // 字符的坐标
int n = s.size(), mindis = INT_MAX;
int dp[301][27][27];
memset(dp, 0x7f, sizeof(dp));
dp[0][s[0] - 'A' + 1][0] = 0; // 输入第一个字符给手指1
for (int i = 1; i int c = s[i] - 'A' + 1; // 当前字符序号 1-26
for (int c1 = 1; c1 <= 26; ++c1) {
for (int c2 = 0; c2 <= 26; ++c2) {
if (dp[i-1][c1][c2] == 0x7f7f7f7f) continue; // 状态不存在
// 当前字母c由第1个手指输入
dp[i][c][c2] = min(dp[i][c][c2], dp[i-1][c1][c2] + abs(pos[c][0] - pos[c1][0]) + abs(pos[c][1] - pos[c1][1]));
// 当前字母c由第2个手指输入
if (c2 == 0) // 第二个手指还没输入
dp[i][c1][c] = min(dp[i][c1][c], dp[i-1][c1][c2]); // 第一次输入没有成本
else
dp[i][c1][c] = min(dp[i][c1][c], dp[i-1][c1][c2] + abs(pos[c][0] - pos[c2][0]) + abs(pos[c][1] - pos[c2][1]));
}
}
}
for (int c1 = 1; c1 <= 26; ++c1)
for (int c2 = 0; c2 <= 26; ++c2)
mindis = min(mindis, dp[n-1][c1][c2]);
return mindis;
}
};
```
### 性能分析
上述代码的时间复杂度为O(n * 26^2),空间复杂度为O(n * 26^2),其中n是字符串长度。实际运行时间为32ms,内存消耗为7.5MB。
更多内容请关注我的博客和公众号,一起学习进步!
推荐阅读
-
Java并发编程实践目录并发编程01——ThreadLocal并发编程02——ConcurrentHashMap并发编程03——阻塞队列和生产者-消费者模式并发编程04——闭锁Co ...
[详细]
蜡笔小新 2024-12-21 12:39:07
-
本文介绍如何使用Java编写一个程序,通过10个线程分别计算不同区间的和,并最终汇总所有线程的结果。每个线程负责计算一段连续的整数之和,最后将所有线程的结果相加。 ...
[详细]
蜡笔小新 2024-12-21 10:32:48
-
-
在设计模式中,动态代理模式是应用最为广泛的一种代理模式。它允许我们在运行时动态创建代理对象,并在调用方法时进行增强处理。本文将详细介绍动态代理的实现机制及其应用场景。 ...
[详细]
蜡笔小新 2024-12-21 15:46:52
-
本文详细介绍了ESFramework框架中AgileTcp组件的设计与实现。AgileTcp是ESFramework提供的ITcp接口的高效实现,旨在优化TCP通信的性能和结构清晰度。 ...
[详细]
蜡笔小新 2024-12-21 13:56:06
-
本文深入探讨了 Java 中 LocalTime 类的 isSupported() 方法,包括其功能、语法和使用示例。通过具体的代码片段,帮助读者理解如何检查特定的时间字段或单位是否被 LocalTime 类支持。 ...
[详细]
蜡笔小新 2024-12-21 13:49:39
-
本文介绍了 Python 的 Pmagick 库中用于图像处理的木炭滤镜方法,探讨其功能和用法,并通过实例演示如何应用该方法。 ...
[详细]
蜡笔小新 2024-12-21 13:44:30
-
为了解决不同服务器间共享图片的需求,我们最初考虑建立一个FTP图片服务器。然而,考虑到项目是一个简单的CMS系统,为了简化流程,团队决定探索七牛云存储的解决方案。本文将详细介绍使用七牛云存储的过程和心得。 ...
[详细]
蜡笔小新 2024-12-21 13:15:50
-
二叉树很重要树是数据结构中的重中之重,尤其以各类二叉树为学习的难点。单就面试而言,在 ...
[详细]
蜡笔小新 2024-12-21 13:13:13
-
本文详细介绍了MySQL中常用的七种JOIN查询方法,包括内连接、左外连接、右外连接、全外连接以及排除连接等,并通过实例进行说明。 ...
[详细]
蜡笔小新 2024-12-21 12:53:17
-
本文详细介绍了在 Android 6.0 系统中切换到指定 Wi-Fi 的方法,包括常见的问题、原因分析及解决方案。通过官方文档和代码示例,帮助开发者更好地理解和实现这一功能。 ...
[详细]
蜡笔小新 2024-12-21 11:36:34
-
本文将详细探讨RDMA架构中的关键组件——队列对(Queue Pair,简称QP),包括其基本概念、硬件与软件实现、QPC的作用、QPN的分配机制以及用户接口和状态机。通过这些内容,读者可以更全面地理解QP在RDMA通信中的重要性和工作原理。 ...
[详细]
蜡笔小新 2024-12-21 11:16:36
-
本文介绍了一种基于选择排序思想的高效排序方法——堆排序。通过使用堆数据结构,堆排序能够在每次查找最大元素时显著提高效率。文章详细描述了堆排序的工作原理,并提供了完整的C语言代码实现。 ...
[详细]
蜡笔小新 2024-12-21 11:14:55
-
本文详细介绍了 Android 中基于消息传递的进程间通信(IPC)机制——Messenger。通过实例和源码分析,帮助开发者更好地理解和使用这一高效的通信工具。 ...
[详细]
蜡笔小新 2024-12-21 11:11:40
-
本文探讨了如何在Classic ASP中实现与PHP的hash_hmac('SHA256', $message, pack('H*', $secret))函数等效的哈希生成方法。通过分析不同实现方式及其产生的差异,提供了一种使用Microsoft .NET Framework的解决方案。 ...
[详细]
蜡笔小新 2024-12-21 10:38:09
-
本文探讨了在 SQL Server 中使用 JDBC 插入数据时遇到的问题。通过详细分析代码和数据库配置,提供了解决方案并解释了潜在的原因。 ...
[详细]
蜡笔小新 2024-12-21 09:52:27
-