力扣每日一题0223-仅仅反转字母

134 阅读1分钟

「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」。

给你一个字符串 s ,根据下述规则反转字符串:

  • 所有非英文字母保留在原有位置。
  • 所有英文字母(小写或大写)位置反转。

返回反转后的 s

示例 1:

输入:s = "ab-cd"
输出:"dc-ba"

示例 2:

输入:s = "a-bC-dEf-ghIj"
输出:"j-Ih-gfE-dCba"

示例 3:

输入:s = "Test1ng-Leet=code-Q!"
输出:"Qedo1ct-eeLg=ntse-T!"

提示

  • 1 <= s.length <= 100
  • s 仅由 ASCII 值在范围 [33, 122] 的字符组成
  • s 不含 '\"''\\'

HashMap

由于非字母需要保留在原地,所以我们需要先找到非字母的字符,以及该字符所在的位置,并将其记录下来,然后将所有字母字符入队。

之后,我们需要出队字母字符,并判断当前位置是否应为非字母字符

  • 当前位置是非字母字符,取出非字母字符,放入新数组中
  • 如果当前位置是字母字符,出队字母字符
  • 直至字母队为空,非字母哈希为空结束。
var reverseOnlyLetters = function (s) {
  const len = s.length
  const map = new Map()
  const arr = []
  const res = []
  for (let i = 0; i < len; i++) {
    if ((/^[a-zA-Z]+$/.test(s[i]))) arr.unshift(s[i])
    else map.set(i, s[i])
  }
  while (map.size != 0 || arr.length != 0) {
    if (map.has(res.length)) {
      res.push(map.get(res.length))
      map.delete(res.length - 1)
    }
    else if (arr.length != 0) res.push(arr.shift())
  }
  return res.join("");
};

双指针

我们使用 leftleft 指针从左边开始扫描字符串 s,rightright 指针从右边开始扫描字符串 s。如果两个指针都扫描到字母,且 left<rightleft<right,那么交换 s[left]s[left]s[right]s[right],然后继续进行扫描;否则表明反转过程结束,返回处理后的字符串。

var reverseOnlyLetters = function(s) {
    const n = s.length;
    const arr = [...s];
    let left = 0, right = n - 1;
    while (true) {
        while (left < right && !(/^[a-zA-Z]+$/.test(s[left]))) { // 判断左边是否扫描到字母
            left++;
        }
        while (right > left && !(/^[a-zA-Z]+$/.test(s[right]))) { // 判断右边是否扫描到字母
            right--;
        }
        if (left >= right) {
            break;
        }
        swap(arr, left, right);
        left++;
        right--;
    }
    return arr.join('');
};

const swap = (arr, left, right) => {
    const temp = arr[left];
    arr[left] = arr[right];
    arr[right] = temp;
}