字母移位 II

154 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第24天,点击查看活动详情

问题描述

给你一个小写英文字母组成的字符串 s 和一个二维整数数组 shifts ,其中 shifts[i] = [starti, endi, directioni] 。对于每个 i ,将 s 中从下标 starti 到下标 endi (两者都包含)所有字符都进行移位运算,如果 directioni = 1 将字符向后移位,如果 directioni = 0 将字符向前移位。

将一个字符 向后 移位的意思是将这个字符用字母表中 下一个 字母替换(字母表视为环绕的,所以 'z' 变成 'a')。类似的,将一个字符 向前 移位的意思是将这个字符用字母表中 前一个 字母替换(字母表是环绕的,所以 'a' 变成 'z' )。

请你返回对 s 进行所有移位操作以后得到的最终字符串。

示例 1:

输入: s = "abc", shifts = [[0,1,0],[1,2,1],[0,2,1]]
输出: "ace"
解释: 首先,将下标从 0 到 1 的字母向前移位,得到 s = "zac" 。
然后,将下标从 1 到 2 的字母向后移位,得到 s = "zbd" 。
最后,将下标从 0 到 2 的字符向后移位,得到 s = "ace"

示例 2:

输入: s = "dztz", shifts = [[0,0,0],[1,1,1]]
输出: "catz"
解释: 首先,将下标从 0 到 0 的字母向前移位,得到 s = "cztz" 。
最后,将下标从 1 到 1 的字符向后移位,得到 s = "catz"

提示:

  • 1 <= s.length, shifts.length <= 5 * 104
  • shifts[i].length == 3
  • 0 <= starti <= endi < s.length
  • 0 <= directioni <= 1
  • s 只包含小写英文字母。

思路分析

首先我们要先理解一下题意,题目会给我们一个字符串 s 和一个二维整数数组 shifts,其中 shifts[i] = [starti, endi, directioni] 。对于每个 i ,将 s 中从下标 starti 到下标 endi (两者都包含)所有字符都进行移位运算,如果 directioni = 1 将字符向后移位,如果 directioni = 0 将字符向前移位。也就是说我们要根据shifts数组的描述对s字符串进行移位操作,操作方式只有两种:

  • 1、向前移位,如a -> zz -> a
  • 2、向后移位,如a -> b,z -> a

每次操作我们需要对s 中从下标 starti 到下标 endi区间中的所有字符进行移位,这是一个很明显的区间操作,我们可以使用差分数组来进行解题。如果需要对LR范围内所有数都进行相同的操作,我们不需要从LR遍历arr然后在每个值上进行相同操作,只需要在差分数组d中改变L和R+1的值即可,最后再进行一次前缀和计算便可以将修改后的数组还原出来。

  • 1、构建差分数组

如果需要对LR范围内所有数都进行相同的操作,我们不需要从LR遍历arr然后在每个值上进行相同操作,只需要在差分数组d中改变L和R+1的值即可。

let res = new Array(s.length + 1).fill(0);
for(let i = 0; i < shifts.length; i++){
    res[shifts[i][0]] += (shifts[i][2] == 0 ? -1 : 1);
    res[shifts[i][1] + 1] -= (shifts[i][2] == 0 ? -1 : 1);
}
  • 2、前缀和还原

使用前缀和计算可以将所有位置的操作数计算出来。

for(let i = 0; i < s.length; i++){
    res[i] += (res[i - 1] || 0);
}
  • 3、计算操作后的字符

得到了每个字符的操作记录之后,我们就可以对每个字符进行对应的移位操作得到移位后的新字符。

let ans = '';
for(let i = 0; i < s.length; i++){
    res[i] += (res[i - 1] || 0);
    while(res[i] < 0) res[i] += 26;
    res[i] %= 26; 
    let code = s[i].charCodeAt() + res[i];
    if(code > 122) code = code % 123 + 97;
    ans += String.fromCharCode(code);
}

AC代码

完整代码如下:

/**
 * @param {string} s
 * @param {number[][]} shifts
 * @return {string}
 */
 var shiftingLetters = function(s, shifts) {
    let res = new Array(s.length + 1).fill(0);
    for(let i = 0; i < shifts.length; i++){
        res[shifts[i][0]] += (shifts[i][2] == 0 ? -1 : 1);
        res[shifts[i][1] + 1] -= (shifts[i][2] == 0 ? -1 : 1);
    }
    let ans = '';
    for(let i = 0; i < s.length; i++){
        res[i] += (res[i - 1] || 0);
        while(res[i] < 0) res[i] += 26;
        res[i] %= 26; 
        let code = s[i].charCodeAt() + res[i];
        if(code > 122) code = code % 123 + 97;
        ans += String.fromCharCode(code);
    }
    return ans;
};

说在后面

本人为算法业余爱好者,平时只是随着兴趣偶尔刷刷题,如果上面分享有错误的地方,欢迎指出,感激不尽。