力扣链接:3614. 处理包含特殊操作的字符串 II,难度等级:困难。
给你一个由小写英文字母和特殊字符 '*'、'#' 和 '%' 组成的字符串 s。
同时给你一个整数 k。
通过从左到右处理 s ,按照以下规则构建一个新字符串 result :
- 如果字符是小写英文字母,将其追加到
result的末尾。 '*'删除result的最后一个字符(如果存在)。'#'复制 当前的result并将其追加到自身末尾。'%'反转 当前的result。
返回最终字符串 result 的第 k 个字符(从 0 开始计数,或者根据题意如果是0-indexed,请视同 0-indexed)。如果 k 超出 result 的边界,则返回 '.'。
示例 1:
输入: s = "a#b%*", k = 1
输出: "a"
解释:
i |
s[i] |
操作 | 当前 result |
|---|---|---|---|
| 0 | 'a' |
追加 'a' |
"a" |
| 1 | '#' |
复制 result |
"aa" |
| 2 | 'b' |
追加 'b' |
"aab" |
| 3 | '%' |
反转 result |
"baa" |
| 4 | '*' |
删除最后一个字符 | "ba" |
最终的 result 是 "ba"。索引 k = 1 处的字符是 'a'。
示例 2:
输入: s = "cd%#*#", k = 3
输出: "d"
解释:
i |
s[i] |
操作 | 当前 result |
|---|---|---|---|
| 0 | 'c' |
追加 'c' |
"c" |
| 1 | 'd' |
追加 'd' |
"cd" |
| 2 | '%' |
反转 result |
"dc" |
| 3 | '#' |
复制 result |
"dcdc" |
| 4 | '*' |
删除最后一个字符 | "dcd" |
| 5 | '#' |
复制 result |
"dcddcd" |
最终的 result 是 "dcddcd"。索引 k = 3 处的字符是 'd'。
示例 3:
输入: s = "z*#", k = 0
输出: "."
解释:
i |
s[i] |
操作 | 当前 result |
|---|---|---|---|
| 0 | 'z' |
追加 'z' |
"z" |
| 1 | '*' |
删除最后一个字符 | "" |
| 2 | '#' |
复制字符串 | "" |
最终的 result 是 ""。因为索引 k = 0 越界,输出为 '.'。
约束:
1 <= s.length <= 10^5s仅由小写英文字母和特殊字符'*'、'#'以及'%'组成。0 <= k <= 10^{15}- 处理
s后result的长度不会超过10^{15}。
提示 1
Track the length of the string after each operation on s.
提示 2
Walk backwards through s, undoing each # by using modulus on the tracked lengths, and undoing each % by mirroring across the midpoint, to pinpoint the kth character.
思路
由于 k 的最大值可以达到 10^{15},我们无法直接模拟字符串的构建过程,否则会导致内存超限(MLE)或时间超限(TLE)。
相反,我们可以使用反向模拟法:
- 首先,计算在处理
s的每一步时字符串的长度。 - 如果最终长度小于或等于
k,说明索引越界,直接返回'.'。 - 否则,我们从后往前遍历字符串。在每一步
i:- 如果
s[i]是字母:检查它是否是我们要找的字符(即lens[i] - 1 == k)。如果是,则返回它。 - 如果
s[i] == '#'(复制):字符串被复制了一份。如果k落在后半部分,它对应于前半部分的同一个字符。所以我们更新k = k % (lens[i] / 2)。 - 如果
s[i] == '%'(反转):字符串被反转了。我们将k更新为反转前的对应索引:k = lens[i] - 1 - k。 - 如果
s[i] == '*'(删除):我们不需要改变k。因为k是当前字符串中的一个有效索引,而*只是删除了最末尾的一个字符,我们当前的k自然指向在此*之前添加的字符。
- 如果
这个逻辑保证了 k 始终是当前步骤字符串中的一个有效索引。
👋 站长寄语:我还为你开发了 coder.id - 打造程序员个人IP, 包含作品集、简历和博客等,助您顺利拿到 Offer!
步骤
- 创建一个大小为
n的数组lens,用于存储每一步的字符串长度。 - 初始化
curr_len = 0。 - 从左到右遍历
s:- 如果
s[i]是字母,curr_len加 1。 - 如果
s[i] == '*',curr_len减 1(最小为 0)。 - 如果
s[i] == '#',curr_len乘以 2。 - 将
curr_len存入lens[i]。
- 如果
- 如果
curr_len <= k,返回'.'。 - 从右到左遍历
s:- 如果
s[i]是字母且lens[i] - 1 == k,返回s[i]。 - 如果
s[i] == '#'且k >= lens[i] / 2,更新k %= (lens[i] / 2)。 - 如果
s[i] == '%',更新k = lens[i] - 1 - k。
- 如果
复杂度
时间复杂度
O(n)
空间复杂度
O(n)
解释
- 时间复杂度:
O(n),其中n是字符串s的长度。我们对字符串进行了两次遍历。 - 空间复杂度:
O(n),用于在lens数组中存储每一步的长度。
Python #
class Solution:
def processStr(self, s: str, k: int) -> str:
lens = []
curr_len = 0
for ch in s:
if ch.isalpha():
curr_len += 1
elif ch == '*':
curr_len = max(0, curr_len - 1)
elif ch == '#':
curr_len *= 2
elif ch == '%':
pass
lens.append(curr_len)
if curr_len <= k:
return '.'
for i in range(len(s) - 1, -1, -1):
if s[i].isalpha():
if lens[i] - 1 == k:
return s[i]
elif s[i] == '#':
half = lens[i] // 2
if half > 0 and k >= half:
k %= half
elif s[i] == '%':
if lens[i] > 0:
k = lens[i] - 1 - k
return '.'
Java #
class Solution {
public char processStr(String s, long k) {
int n = s.length();
long[] lens = new long[n];
long currLen = 0;
for (int i = 0; i < n; i++) {
char ch = s.charAt(i);
if (ch >= 'a' && ch <= 'z') {
currLen++;
} else if (ch == '*') {
currLen = Math.max(0L, currLen - 1);
} else if (ch == '#') {
currLen *= 2;
}
lens[i] = currLen;
}
if (currLen <= k) {
return '.';
}
for (int i = n - 1; i >= 0; i--) {
char ch = s.charAt(i);
if (ch >= 'a' && ch <= 'z') {
if (lens[i] - 1 == k) {
return ch;
}
} else if (ch == '#') {
long half = lens[i] / 2;
if (half > 0 && k >= half) {
k %= half;
}
} else if (ch == '%') {
if (lens[i] > 0) {
k = lens[i] - 1 - k;
}
}
}
return '.';
}
}
JavaScript #
/**
* @param {string} s
* @param {number} k
* @return {string}
*/
var processStr = function(s, k) {
const n = s.length;
const lens = new Array(n);
let currLen = 0n;
let bigK = BigInt(k);
for (let i = 0; i < n; i++) {
const ch = s[i];
if (ch >= 'a' && ch <= 'z') {
currLen++;
} else if (ch === '*') {
if (currLen > 0n) currLen--;
} else if (ch === '#') {
currLen *= 2n;
}
lens[i] = currLen;
}
if (currLen <= bigK) {
return '.';
}
for (let i = n - 1; i >= 0; i--) {
const ch = s[i];
if (ch >= 'a' && ch <= 'z') {
if (lens[i] - 1n === bigK) {
return ch;
}
} else if (ch === '#') {
const half = lens[i] / 2n;
if (half > 0n && bigK >= half) {
bigK %= half;
}
} else if (ch === '%') {
if (lens[i] > 0n) {
bigK = lens[i] - 1n - bigK;
}
}
}
return '.';
};
C++ #
class Solution {
public:
char processStr(string s, long long k) {
int n = s.length();
vector<long long> lens(n);
long long curr_len = 0;
for (int i = 0; i < n; ++i) {
char ch = s[i];
if (ch >= 'a' && ch <= 'z') {
curr_len++;
} else if (ch == '*') {
curr_len = max(0LL, curr_len - 1);
} else if (ch == '#') {
curr_len *= 2;
}
lens[i] = curr_len;
}
if (curr_len <= k) return '.';
for (int i = n - 1; i >= 0; --i) {
char ch = s[i];
if (ch >= 'a' && ch <= 'z') {
if (lens[i] - 1 == k) return ch;
} else if (ch == '#') {
long long half = lens[i] / 2;
if (half > 0 && k >= half) k %= half;
} else if (ch == '%') {
if (lens[i] > 0) k = lens[i] - 1 - k;
}
}
return '.';
}
};
C# #
public class Solution {
public char ProcessStr(string s, long k) {
int n = s.Length;
long[] lens = new long[n];
long currLen = 0;
for (int i = 0; i < n; i++) {
char ch = s[i];
if (ch >= 'a' && ch <= 'z') {
currLen++;
} else if (ch == '*') {
currLen = Math.Max(0L, currLen - 1);
} else if (ch == '#') {
currLen *= 2;
}
lens[i] = currLen;
}
if (currLen <= k) return '.';
for (int i = n - 1; i >= 0; i--) {
char ch = s[i];
if (ch >= 'a' && ch <= 'z') {
if (lens[i] - 1 == k) return ch;
} else if (ch == '#') {
long half = lens[i] / 2;
if (half > 0 && k >= half) k %= half;
} else if (ch == '%') {
if (lens[i] > 0) k = lens[i] - 1 - k;
}
}
return '.';
}
}
Go #
func processStr(s string, k int64) byte {
n := len(s)
lens := make([]int64, n)
var currLen int64 = 0
for i := 0; i < n; i++ {
ch := s[i]
if ch >= 'a' && ch <= 'z' {
currLen++
} else if ch == '*' {
if currLen > 0 {
currLen--
}
} else if ch == '#' {
currLen *= 2
}
lens[i] = currLen
}
if currLen <= k {
return '.'
}
for i := n - 1; i >= 0; i-- {
ch := s[i]
if ch >= 'a' && ch <= 'z' {
if lens[i] - 1 == k {
return ch
}
} else if ch == '#' {
half := lens[i] / 2
if half > 0 && k >= half {
k %= half
}
} else if ch == '%' {
if lens[i] > 0 {
k = lens[i] - 1 - k
}
}
}
return '.'
}
Ruby #
# @param {String} s
# @param {Integer} k
# @return {String}
def process_str(s, k)
n = s.length
lens = Array.new(n)
curr_len = 0
(0...n).each do |i|
ch = s[i]
if ch >= 'a' && ch <= 'z'
curr_len += 1
elsif ch == '*'
curr_len = [0, curr_len - 1].max
elsif ch == '#'
curr_len *= 2
end
lens[i] = curr_len
end
return '.' if curr_len <= k
(n - 1).downto(0) do |i|
ch = s[i]
if ch >= 'a' && ch <= 'z'
return ch if lens[i] - 1 == k
elsif ch == '#'
half = lens[i] / 2
k %= half if half > 0 && k >= half
elsif ch == '%'
k = lens[i] - 1 - k if lens[i] > 0
end
end
'.'
end
Rust #
impl Solution {
pub fn process_str(s: String, mut k: i64) -> char {
let n = s.len();
let mut lens = vec![0i64; n];
let mut curr_len = 0i64;
let bytes = s.as_bytes();
for i in 0..n {
let ch = bytes[i];
if ch >= b'a' && ch <= b'z' {
curr_len += 1;
} else if ch == b'*' {
if curr_len > 0 { curr_len -= 1; }
} else if ch == b'#' {
curr_len *= 2;
}
lens[i] = curr_len;
}
if curr_len <= k {
return '.';
}
for i in (0..n).rev() {
let ch = bytes[i];
if ch >= b'a' && ch <= b'z' {
if lens[i] - 1 == k {
return ch as char;
}
} else if ch == b'#' {
let half = lens[i] / 2;
if half > 0 && k >= half {
k %= half;
}
} else if ch == b'%' {
if lens[i] > 0 {
k = lens[i] - 1 - k;
}
}
}
'.'
}
}
Kotlin #
class Solution {
fun processStr(s: String, k: Long): Char {
var mutableK = k
val n = s.length
val lens = LongArray(n)
var currLen = 0L
for (i in 0 until n) {
val ch = s[i]
if (ch in 'a'..'z') {
currLen++
} else if (ch == '*') {
if (currLen > 0) currLen--
} else if (ch == '#') {
currLen *= 2
}
lens[i] = currLen
}
if (currLen <= mutableK) return '.'
for (i in n - 1 downTo 0) {
val ch = s[i]
if (ch in 'a'..'z') {
if (lens[i] - 1 == mutableK) return ch
} else if (ch == '#') {
val half = lens[i] / 2
if (half > 0 && mutableK >= half) mutableK %= half
} else if (ch == '%') {
if (lens[i] > 0) mutableK = lens[i] - 1 - mutableK
}
}
return '.'
}
}
Swift #
class Solution {
func processStr(_ s: String, _ k: Int) -> Character {
var mutableK = k
let chars = Array(s)
let n = chars.count
var lens = [Int](repeating: 0, count: n)
var currLen = 0
for i in 0..<n {
let ch = chars[i]
if ch >= "a" && ch <= "z" {
currLen += 1
} else if ch == "*" {
currLen = max(0, currLen - 1)
} else if ch == "#" {
currLen *= 2
}
lens[i] = currLen
}
if currLen <= mutableK {
return "."
}
for i in stride(from: n - 1, through: 0, by: -1) {
let ch = chars[i]
if ch >= "a" && ch <= "z" {
if lens[i] - 1 == mutableK {
return ch
}
} else if ch == "#" {
let half = lens[i] / 2
if half > 0 && mutableK >= half {
mutableK %= half
}
} else if ch == "%" {
if lens[i] > 0 {
mutableK = lens[i] - 1 - mutableK
}
}
}
return "."
}
}
其它语言
欢迎贡献代码到 GitHub repo -> 3614. 处理包含特殊操作的字符串 II。感谢!打造你的开发者个人IP
- 📄 简历 + 作品集 + 博客
- 🌐 支持绑定自定义域名
- ✨ 顶级子域名:
https://yourname.coder.id