Easy
题目描述
给你一个字符串 s 和一个整数 k。
判断是否存在一个长度恰好为 k 的子串,满足以下条件:
- 子串只由一个不同的字符组成(例如,“aaa” 或 “bbb”)。
- 如果子串前面有字符,该字符必须与子串中的字符不同。
- 如果子串后面有字符,该字符也必须与子串中的字符不同。
如果存在这样的子串,返回 true。否则,返回 false。
示例 1:
输入:s = "aaabaaa", k = 3
输出:true
解释:
子串 s[4..6] == "aaa" 满足条件。
- 长度为 3。
- 所有字符都相同。
- "aaa" 前面的字符是 'b',与 'a' 不同。
- "aaa" 后面没有字符。
示例 2:
输入:s = "abc", k = 2
输出:false
解释:
不存在长度为 2 的子串,由一个不同字符组成且满足条件。
提示:
1 <= k <= s.length <= 100s只由小写英文字母组成
解题思路
这道题要求找到一个长度为 k 的特殊子串,需要同时满足三个条件:
- 子串由相同字符组成
- 前一个字符(如果存在)与子串字符不同
- 后一个字符(如果存在)与子串字符不同
解法一:滑动窗口法(推荐)
使用固定长度为 k 的滑动窗口遍历字符串。对于每个窗口,检查是否所有字符都相同,以及边界条件是否满足。
解法二:暴力枚举法
对每个可能的起始位置,检查从该位置开始长度为 k 的子串是否满足条件。
两种解法的时间复杂度都是 O(n×k),但滑动窗口法在实现上更清晰。由于题目约束较小(n ≤ 100),两种方法都能高效解决。
具体实现时,我们遍历所有可能的起始位置 i(从 0 到 n-k),对每个位置:
- 检查
s[i]到s[i+k-1]是否都相等 - 检查前一个字符
s[i-1](如果存在)是否与当前字符不同 - 检查后一个字符
s[i+k](如果存在)是否与当前字符不同
如果找到满足条件的子串,立即返回 true;遍历完所有位置后仍未找到,则返回 false。
代码实现
class Solution {
public:
bool hasSpecialSubstring(string s, int k) {
int n = s.length();
for (int i = 0; i <= n - k; i++) {
// 检查子串是否由相同字符组成
bool allSame = true;
for (int j = i + 1; j < i + k; j++) {
if (s[j] != s[i]) {
allSame = false;
break;
}
}
if (!allSame) continue;
// 检查前一个字符
if (i > 0 && s[i - 1] == s[i]) continue;
// 检查后一个字符
if (i + k < n && s[i + k] == s[i]) continue;
return true;
}
return false;
}
};
class Solution:
def hasSpecialSubstring(self, s: str, k: int) -> bool:
n = len(s)
for i in range(n - k + 1):
# 检查子串是否由相同字符组成
if not all(s[j] == s[i] for j in range(i, i + k)):
continue
# 检查前一个字符
if i > 0 and s[i - 1] == s[i]:
continue
# 检查后一个字符
if i + k < n and s[i + k] == s[i]:
continue
return True
return False
public class Solution {
public bool HasSpecialSubstring(string s, int k) {
int n = s.Length;
for (int i = 0; i <= n - k; i++) {
// 检查子串是否由相同字符组成
bool allSame = true;
for (int j = i + 1; j < i + k; j++) {
if (s[j] != s[i]) {
allSame = false;
break;
}
}
if (!allSame) continue;
// 检查前一个字符
if (i > 0 && s[i - 1] == s[i]) continue;
// 检查后一个字符
if (i + k < n && s[i + k] == s[i]) continue;
return true;
}
return false;
}
}
var hasSpecialSubstring = function(s, k) {
const n = s.length;
for (let i = 0; i <= n - k; i++) {
// 检查子串是否由相同字符组成
let allSame = true;
for (let j = i + 1; j < i + k; j++) {
if (s[j] !== s[i]) {
allSame = false;
break;
}
}
if (!allSame) continue;
// 检查前一个字符
if (i > 0 && s[i - 1]
复杂度分析
| 复杂度 | 大小 |
|---|---|
| 时间复杂度 | O(n × k) |
| 空间复杂度 | O(1) |
其中 n 是字符串长度。在最坏情况下,需要检查 n-k+1 个起始位置,每个位置需要 O(k) 时间验证子串是否由相同字符组成。