Leetcode每日一题 —— 1320. 二指输入的的最小距离

2026-04-13 12:311阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述:
力扣 LeetCode

1320. 二指输入的的最小距离 - 力扣(LeetCode)

1320. 二指输入的的最小距离 - [https://assets.leetcode.cn/aliyun-lc-upload/uploads/2020/01/11/leetcode_keyboard.png] 二指输入法定制键盘在 X-Y 平面上的布局如上图所示,其中每个大写英文字母都位于某个坐标处。 * 例如字母A位于坐标(0,0),字母B位于坐标(0,1),字母P位于坐标(2,3)且字母...

思路

看上去是困难题,但是先别慌,输入规模不算很大,可以先试试 DFS 和记忆化。

首先要搞清楚我们要维护某一时刻的哪些状态:

  1. 手指 1 落在的字符
  2. 手指 2 落在的字符
  3. 已经输入到了 word 中的哪个字符

可以看到我们可以顺着“输入 word 中的字符”这条路来拆分子问题来递归求解:每要输入一个字符时,要不用第一根手指,要不用第二根手指

值得注意的一点是,为了让初始代价最小,手指输入的第一个字符的代价可以忽略掉(假设这根手指最开始就在这个字符上)。

怎么判断手指是不是第一次输入呢?咱的想法是在 0-25 之外再加个状态值 26,用 26 表示手指还没输入过一个字符。

直接用 DFS 很容易超时,幸运的是上面我们把状态找出来后,做记忆化就很容易了,开个 memo[n][27][27] 数组即可。

∘₊✧───────────────────────✧₊∘

代码

class Solution { public: int minimumDistance(string word) { // 曼哈顿距离 // 输入必须按字符顺序来,但是两根手指可以交替使用 // 两根手指最开始应该都落在 word 中的字符上,这样最开始输入的就可以是 0 代价 int n=word.size(); // 便捷方法,计算两个字符在网格中的距离 auto cDist=[&](char c1, char c2) -> int{ int c1Pos=c1-'A'; int c2Pos=c2-'A'; return abs(c1Pos/6 - c2Pos/6) + abs(c1Pos%6 - c2Pos%6); }; // 记忆化搜索 int memo[n][27][27]; for(int i=0;i<n;i++){ for(int j=0;j<27;j++){ for(int k=0;k<27;k++){ memo[i][j][k]=-1; } } } // word[idx] 为当前要输入的字符 // f1 为目前第一根手指落在的字符,f2 则是第二根手指所落的字符编号 (0-25), 为 26 说明还没用过这个手指 auto dfs=[&](auto&& self,int idx,int f1,int f2) -> int{ if(idx>=n){ // 输入完了 return 0; } if(memo[idx][f1][f2]!=-1){ // 记忆化 return memo[idx][f1][f2]; } // 这个字符由第一根手指输入 int firstCost=0; if(f1!=26){ // 不是第一次用第一根手指,则可能要移动手指 firstCost=cDist('A'+f1,word[idx]); } firstCost+=self(self,idx+1,word[idx]-'A',f2); // 这个字符若由第二根手指输入 int secondCost=0; if(f2!=26){ secondCost=cDist('A'+f2,word[idx]); } secondCost+=self(self,idx+1,f1,word[idx]-'A'); // 取二者中最小代价 int res= min(firstCost,secondCost); memo[idx][f1][f2]=res; return res; }; return dfs(dfs,0,26,26); } };

看到 memo[n][27][27] 这玩意,很明显这题也可以按动态规划来写了 (╭ರ_•́)。


继续复习一些题

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

递归写法:

class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { // 递归当然是可以实现的 auto rm=[&](auto&& self, ListNode* node) -> int { if(node==nullptr){ return 0; } int cnt=self(self, node->next); if(cnt==n&&node->next!=nullptr){ node->next=node->next->next; } return cnt+1; }; int cnt=rm(rm,head); if(cnt==n){ // 首个节点被移除 return head->next; }else{ return head; } } };

双指针写法,比较巧(符合题目要求的“扫描一遍”):

class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { // 可以用快慢指针 // 快指针先走 n,保持 fast 与 slow 指向的节点的间隔为 n,一起后移直至 fast 为 null ListNode* dummy=new ListNode(); // 头结点,防止要删除的是 head dummy->next=head; ListNode *fast=dummy,*slow=dummy; while(n>0){ fast=fast->next; n--; } while(fast->next!=nullptr){ slow=slow->next; fast=fast->next; } // 此时 slow 下一个节点就是要删的 slow->next=slow->next->next; return dummy->next; } };


108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)

  • 递归建树,BST 中序序列正好是有序序列,二分即可。

class Solution { public: TreeNode* sortedArrayToBST(vector<int>& nums) { // BST 中序遍历结果就是有序序列,可以这样不断二分去构造树 auto build=[&](auto&& self,int left,int right) -> TreeNode* { if(left==right){ return new TreeNode(nums[left]); }else if(left>right){ return nullptr; } int mid=(left+right)>>1; TreeNode* node=new TreeNode(nums[mid]); node->left=self(self,left,mid-1); node->right=self(self,mid+1,right); return node; }; return build(build,0,nums.size()-1); } };


543. 二叉树的直径 - 力扣(LeetCode)

  • 注意长度是用边数表示的。

class Solution { public: int diameterOfBinaryTree(TreeNode* root) { int res=0; auto getHeight=[&](auto&& self,TreeNode* node) -> int{ if(node==nullptr){ return 0; } int leftH=self(self,node->left); int rightH=self(self,node->right); // 注意长度是用边数表示的 res=max(res,leftH+rightH); return max(leftH,rightH)+1; }; getHeight(getHeight,root); return res; } };

网友解答:
--【壹】--:

没写出来嘻嘻,hard的压迫感还是太强了。