1. 题目链接
2. 题目描述
给定一个仅包含小写字母和问号 '?'
的字符串 s
,要求将所有 '?'
替换为任意小写字母,使得替换后的字符串中 没有相邻的两个字符相同。
示例:
- 输入:
s = "?zs"
→ 输出:"azs"
(第一个'?'
替换为'a'
)。 - 输入:
s = "ubv?w"
→ 输出:"ubvaw"
('?'
替换为'a'
)。
3. 示例分析
- 简单替换:
- 输入:
"a?b"
→ 输出:"acb"
('?'
替换为'c'
)。
- 输入:
- 边界处理:
- 输入:
"??"
→ 输出:"ab"
(两个'?'
分别替换为'a'
和'b'
)。
- 输入:
- 复杂替换:
- 输入:
"a?a"
→ 输出:"aba"
(中间的'?'
替换为'b'
)。
- 输入:
4. 算法思路
核心思想:
- 遍历字符串:
- 从左到右逐个字符处理,遇到
'?'
时进行替换。
- 从左到右逐个字符处理,遇到
- 字符选择策略:
- 从
'a'
到'z'
依次尝试,选择第一个满足以下条件的字符:- 与左侧字符不同(若存在)。
- 与右侧字符不同(若存在)。
- 从
- 左右判断:
- 每次替换只关注当前字符的左右邻居,确保局部最优,从而保证全局最优。
时间复杂度:O(n * 26) → O(n),其中 n
为字符串长度。
空间复杂度:O(1),无需额外空间。
5. 边界条件与注意事项
- 边界处理:
- 当
'?'
位于字符串开头时,只需保证与右侧字符不同。 - 当
'?'
位于字符串末尾时,只需保证与左侧字符不同。
- 当
- 字符范围:
- 仅需替换为小写字母
'a'-'z'
,无需处理其他字符。
- 仅需替换为小写字母
- 相邻字符冲突:
- 若左右字符相同(如
"a?a"
),中间的'?'
必须选择一个与两者不同的字符。
- 若左右字符相同(如
6. 代码实现
class Solution {
public:
string modifyString(string s) {
for (int i = 0; i < s.size(); i++) {
if (s[i] == '?') {
// 遍历 'a'-'z',寻找可替换字符
for (char ch = 'a'; ch <= 'z'; ch++) {
bool leftOk = (i == 0) || (s[i-1] != ch); // 左侧无冲突
bool rightOk = (i == s.size()-1) || (s[i+1] != ch); // 右侧无冲突
if (leftOk && rightOk) {
s[i] = ch;
break; // 找到第一个可行字符后立即终止
}
}
}
}
return s;
}
};
关键代码解析
遍历字符串:
for (int i = 0; i < s.size(); i++)
- 逐个字符检查是否为
'?'
。
- 逐个字符检查是否为
字符替换逻辑:
for (char ch = 'a'; ch <= 'z'; ch++)
- 从
'a'
到'z'
依次尝试,找到第一个满足条件的字符。
- 从
条件检查:
bool leftOk = (i == 0) || (s[i-1] != ch); bool rightOk = (i == s.size()-1) || (s[i+1] != ch);
leftOk
:若'?'
在开头,无需检查左侧;否则检查左侧字符是否不同。rightOk
:若'?'
在末尾,无需检查右侧;否则检查右侧字符是否不同。
替换并终止:
if (leftOk && rightOk) { s[i] = ch; break; }
- 找到第一个可行字符后立即替换并跳出循环,保证时间复杂度最优。
与其他解法的对比
方法 | 时间复杂度 | 空间复杂度 | 核心思想 |
---|---|---|---|
模拟算法 | O(n) | O(1) | 逐个替换,选择第一个可行字符 |
预填充法 | O(n) | O(1) | 预先处理所有 '?' 的位置 |
随机替换法 | O(n) | O(1) | 随机选择字符,可能需重试 |