Medium
题目描述
给定一个编码字符串 s。为了将字符串解码成磁带,需要逐个字符读取编码字符串,并执行以下步骤:
- 如果读取的字符是字母,则将该字母写入磁带。
- 如果读取的字符是数字
d,则将当前磁带的内容重复写入d - 1次(总共重复d次)。
给定一个整数 k,返回解码字符串中第 k 个字母(从 1 开始索引)。
示例 1:
输入: s = "leet2code3", k = 10
输出: "o"
解释: 解码字符串为 "leetleetcodeleetleetcodeleetleetcode"。
字符串中第 10 个字母是 "o"。
示例 2:
输入: s = "ha22", k = 5
输出: "h"
解释: 解码字符串为 "hahahaha"。
第 5 个字母是 "h"。
示例 3:
输入: s = "a2345678999999999999999", k = 1
输出: "a"
解释: 解码字符串是 "a" 重复 8301530446056247680 次。
第 1 个字母是 "a"。
提示:
2 <= s.length <= 100s由小写英文字母和数字 2-9 组成s以字母开头1 <= k <= 10^9- 保证
k小于等于解码字符串的长度 - 解码字符串的长度保证小于
2^63
解题思路
这道题的关键是理解解码过程和避免实际构建完整的解码字符串(因为它可能非常长)。
核心思路:
- 正向计算总长度:首先遍历编码字符串,计算解码后的总长度,但不实际构建字符串
- 逆向查找目标字符:从后往前处理,通过数学计算确定第k个字符的位置
算法步骤:
- 第一遍遍历:计算解码后字符串的总长度
- 第二遍从后往前遍历:
- 如果遇到数字d,说明当前段被重复了d次,我们可以通过取模运算确定k在哪个重复段中
- 如果遇到字母且当前位置正好是k,直接返回该字母
- 不断缩小搜索范围直到找到答案
为什么这样做有效?
- 避免了构建巨大的字符串,节省内存
- 通过数学计算快速定位,时间复杂度只与编码字符串长度相关
- 利用重复结构的规律,通过取模运算快速跳转
这种逆向思维的解法是处理大规模重复结构问题的经典方法。
代码实现
class Solution {
public:
string decodeAtIndex(string s, int k) {
long long size = 0;
// 计算解码后字符串的总长度
for (char c : s) {
if (isdigit(c)) {
size *= (c - '0');
} else {
size++;
}
}
// 从后往前处理
for (int i = s.length() - 1; i >= 0; i--) {
k %= size;
if (k == 0 && isalpha(s[i])) {
return string(1, s[i]);
}
if (isdigit(s[i])) {
size /= (s[i] - '0');
} else {
size--;
}
}
return "";
}
};
class Solution:
def decodeAtIndex(self, s: str, k: int) -> str:
size = 0
# 计算解码后字符串的总长度
for c in s:
if c.isdigit():
size *= int(c)
else:
size += 1
# 从后往前处理
for c in reversed(s):
k %= size
if k == 0 and c.isalpha():
return c
if c.isdigit():
size //= int(c)
else:
size -= 1
return ""
public class Solution {
public string DecodeAtIndex(string s, int k) {
long size = 0;
// 计算解码后字符串的总长度
foreach (char c in s) {
if (char.IsDigit(c)) {
size *= (c - '0');
} else {
size++;
}
}
// 从后往前处理
for (int i = s.Length - 1; i >= 0; i--) {
k = (int)(k % size);
if (k == 0 && char.IsLetter(s[i])) {
return s[i].ToString();
}
if (char.IsDigit(s[i])) {
size /= (s[i] - '0');
} else {
size--;
}
}
return "";
}
}
/**
* @param {string} s
* @param {number} k
* @return {string}
*/
var decodeAtIndex = function(s, k) {
let size = 0;
// Calculate the total size of decoded string
for (let c of s) {
if (isNaN(c)) {
size++;
} else {
size *= parseInt(c);
}
}
// Work backwards to find the character
for (let i = s.length - 1; i >= 0; i--) {
k %= size;
if (k === 0 && isNaN(s[i])) {
return s[i];
}
if (isNaN(s[i])) {
size--;
} else {
size /= parseInt(s[i]);
}
}
return "";
};
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 需要遍历编码字符串两次,n为字符串长度 |
| 空间复杂度 | O(1) | 只使用了常数额外空间 |