Hard
题目描述
字符串的方差定义为字符串中任意两个字符出现次数的最大差值。注意这两个字符可以相同,也可以不同。
给定一个只包含小写英文字母的字符串 s,返回 s 的所有子字符串中可能的最大方差。
子字符串是字符串中的一个连续字符序列。
示例 1:
输入:s = "aababbb"
输出:3
解释:
所有可能的方差及其对应的子字符串如下所示:
- 方差为 0 的子字符串:"a", "aa", "ab", "abab", "aababb", "ba", "b", "bb", "bbb"。
- 方差为 1 的子字符串:"aab", "aba", "abb", "aabab", "ababb", "aababbb", "bab"。
- 方差为 2 的子字符串:"aaba", "ababbb", "abbb", "babb"。
- 方差为 3 的子字符串:"babbb"。
由于最大可能方差为 3,所以返回 3。
示例 2:
输入:s = "abcde"
输出:0
解释:
s 中没有字母出现超过一次,所以每个子字符串的方差都是 0。
约束条件:
1 <= s.length <= 10^4s只包含小写英文字母
解题思路
这道题的核心思想是将问题转化为Kadane算法的变种。
主要思路:
枚举字符对:枚举所有可能的字符对 (major, minor),其中 major 是出现次数较多的字符,minor 是出现次数较少的字符。
转化为数组问题:将字符串转化为数值数组,major 字符记为 +1,minor 字符记为 -1,其他字符记为 0。这样方差就转化为了子数组的最大和。
使用Kadane算法的变种:需要确保子字符串中至少包含一个 minor 字符,否则方差为 0。维护两个状态:
hasMinor = false:当前子数组还没有遇到 minor 字符hasMinor = true:当前子数组已经包含了 minor 字符
状态转移:
- 遇到 major:两种状态的和都增加 1
- 遇到 minor:
hasMinor = false状态转为hasMinor = true,和变为 -1;hasMinor = true状态的和减少 1 - 遇到其他字符:保持不变
重置条件:当
hasMinor = true的状态和变为负数时,重置为 0,但保持hasMinor = true状态。
这种方法确保我们能够找到包含指定字符对的子字符串的最大方差。
代码实现
class Solution {
public:
int largestVariance(string s) {
vector<int> counter(26, 0);
for (char c : s) {
counter[c - 'a']++;
}
int maxVariance = 0;
for (int major = 0; major < 26; major++) {
for (int minor = 0; minor < 26; minor++) {
if (major == minor || counter[major] == 0 || counter[minor] == 0) {
continue;
}
int maxEndingHere = 0;
int maxSoFar = 0;
bool hasMinor = false;
bool firstMinor = false;
for (char c : s) {
if (c == 'a' + major) {
maxEndingHere++;
} else if (c == 'a' + minor) {
hasMinor = true;
firstMinor = true;
maxEndingHere--;
}
if (hasMinor) {
maxSoFar = max(maxSoFar, maxEndingHere);
}
if (maxEndingHere < 0 && hasMinor) {
maxEndingHere = 0;
hasMinor = false;
}
}
maxVariance = max(maxVariance, maxSoFar);
}
}
return maxVariance;
}
};
class Solution:
def largestVariance(self, s: str) -> int:
counter = [0] * 26
for c in s:
counter[ord(c) - ord('a')] += 1
max_variance = 0
for major in range(26):
for minor in range(26):
if major == minor or counter[major] == 0 or counter[minor] == 0:
continue
max_ending_here = 0
max_so_far = 0
has_minor = False
first_minor = False
for c in s:
if ord(c) - ord('a') == major:
max_ending_here += 1
elif ord(c) - ord('a') == minor:
has_minor = True
first_minor = True
max_ending_here -= 1
if has_minor:
max_so_far = max(max_so_far, max_ending_here)
if max_ending_here < 0 and has_minor:
max_ending_here = 0
has_minor = False
max_variance = max(max_variance, max_so_far)
return max_variance
public class Solution {
public int LargestVariance(string s) {
int[] counter = new int[26];
foreach (char c in s) {
counter[c - 'a']++;
}
int maxVariance = 0;
for (int major = 0; major < 26; major++) {
for (int minor = 0; minor < 26; minor++) {
if (major == minor || counter[major] == 0 || counter[minor] == 0) {
continue;
}
int maxEndingHere = 0;
int maxSoFar = 0;
bool hasMinor = false;
bool firstMinor = false;
foreach (char c in s) {
if (c == 'a' + major) {
maxEndingHere++;
} else if (c == 'a' + minor) {
hasMinor = true;
firstMinor = true;
maxEndingHere--;
}
if (hasMinor) {
maxSoFar = Math.Max(maxSoFar, maxEndingHere);
}
if (maxEndingHere < 0 && hasMinor) {
maxEndingHere = 0;
hasMinor = false;
}
}
maxVariance = Math.Max(maxVariance, maxSoFar);
}
}
return maxVariance;
}
}
var largestVariance = function(s) {
const chars = [...new Set(s)];
let maxVariance = 0;
for (let i = 0; i < chars.length; i++) {
for (let j = 0; j < chars.length; j++) {
if (i === j) continue;
const char1 = chars[i];
const char2 = chars[j];
let count1 = 0, count2 = 0;
let hasChar2 = false;
for (let k = 0; k < s.length; k++) {
if (s[k] === char1) count1++;
if (s[k] === char2) {
count2++;
hasChar2 = true;
}
if (hasChar2) {
maxVariance = Math.max(maxVariance, count1 - count2);
}
if (count1 < count2) {
count1 = 0;
count2 = 0;
hasChar2 = false;
}
}
}
}
return maxVariance;
};
复杂度分析
| 复杂度 | 大小 |
|---|---|
| 时间复杂度 | O(26² × n) = O(n) |
| 空间复杂度 | O(1) |
其中 n 是字符串的长度。虽然有两层循环枚举字符对,但由于只有26个小写字母,所以常数因子为 26² = 676,整体时间复杂度仍为线性。
相关题目
- . Maximum Subarray (Medium)