[ 简单模拟 ]821. 字符的最短距离

150 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第19天,点击查看活动详情

每日刷题 2021.04.19

题目

  • 给你一个字符串 s 和一个字符 c ,且 c 是 s 中出现过的字符。
  • 返回一个整数数组 answer ,其中 answer.length == s.length 且 answer[i] 是 s 中从下标 i 到离它 最近 的字符 c 的 距离 。
  • 两个下标 i 和 j 之间的 距离 为 abs(i - j) ,其中 abs 是绝对值函数。

示例

  • 示例1
输入:s = "loveleetcode", c = "e"
输出:[3,2,1,0,1,0,0,1,2,2,1,0]
解释:字符 'e' 出现在下标 35611 处(下标从 0 开始计数)。
距下标 0 最近的 'e' 出现在下标 3 ,所以距离为 abs(0 - 3) = 3 。
距下标 1 最近的 'e' 出现在下标 3 ,所以距离为 abs(1 - 3) = 2 。
对于下标 4 ,出现在下标 3 和下标 5 处的 'e' 都离它最近,但距离是一样的 abs(4 - 3) == abs(4 - 5) = 1 。
距下标 8 最近的 'e' 出现在下标 6 ,所以距离为 abs(8 - 6) = 2
  • 示例2
输入: s = "aaab", c = "b"
输出: [3,2,1,0]

提示

  • 1 <= s.length <= 104
  • s[i] 和 c 均为小写英文字母
  • 题目数据保证 c 在 s 中至少出现一次

解题思路

  • 两次遍历:将s字符串从左到右遍历一次,再从右到左遍历一次。
  • 第一次遍历找到当前的每一位i距离左边c最近的距离;第二次遍历找到当前的每一位i距离右边c最近的距离。

具体实现

  • 根据题意可知:返回的结果数组ans,长度和s串相同,ans数组每一项的初始值为0
  • 令初始时,s串中c出现的下标为j = -1,随着循环遍历s串找到真正存在的c的具体下标i,将j = i
  • 对于s串中的每一位都在ans中对应着一个距离值
    • 从左到右遍历,ans[i] = i - index;
    • 从右到左遍历,ans[i] = Math.min(ans[i], Math.abs(i - index));

反思与总结

  • 简单题wa了一发,原因:从右到左的遍历中,如果每次都是取两数的最小值就会出错,因为我们是在初始化的时候将ans数组,全部初始化为0,而并非是其距离c的距离。
  • 修改:题目中既然要找最小的距离,那我们就将ans初始化为最大值,这样就不会影响比较的结果ans[i] = n;

AC代码

var shortestToChar = function(s, c) { 
  // 字符的最短距离
  // 将每个字符左边存储一个值,右边存储一个值
  const len = s.length; 
  // 注意⚠️:将ans数组初始化为len
  let ans = new Array(len).fill(len), index = -1; 
  // 从左到右遍历
  for(let i = 0;i < len; i++) {
    if(s[i] == c){ 
      index = i;
    }
    // 因为初始化过了,因此只有c存在的位置下标需要计算长度
    if(index != -1) ans[i] = i - index; 
  }
  index = -1;
  // 从右到左遍历
  for(let i = len - 1; i >= 0; i--) {
    if(s[i] == c){
      index = i;
    }
    // Math.abs(i - index) 等价于 index - i
    if(index != -1) ans[i] = Math.min(ans[i], Math.abs(i - index))
  }
  return ans; 
};

总结

  • 最小值和最大值的题目,需要考虑:你预设的值,会不会影响结果。
  • 例如:要找最小值,那么你就需要设置一个无穷大;要找最大值,你就需要设置一个无穷小。