Medium
题目描述
如果一个字符串满足以下条件,则称为美丽字符串:
- 字符串中包含所有 5 个英文元音字母(‘a’, ’e’, ‘i’, ‘o’, ‘u’)至少一次
- 字母必须按字母表顺序排列(即所有 ‘a’ 在 ’e’ 之前,所有 ’e’ 在 ‘i’ 之前,等等)
例如,字符串 “aeiou” 和 “aaaaaaeiiiioou” 是美丽字符串,但 “uaeio”、“aeoiu” 和 “aaaeeeooo” 不是美丽字符串。
给定一个由英文元音字母组成的字符串 word,返回 word 中最长美丽子串的长度。如果不存在这样的子串,返回 0。
子串是字符串中连续的字符序列。
示例 1:
输入:word = "aeiaaioaaaaeiiiiouuuooaauuaeiu"
输出:13
解释:word 中最长的美丽子串是 "aaaaeiiiiouuu",长度为 13。
示例 2:
输入:word = "aeeeiiiioooauuuaeiou"
输出:5
解释:word 中最长的美丽子串是 "aeiou",长度为 5。
示例 3:
输入:word = "a"
输出:0
解释:不存在美丽子串,所以返回 0。
约束条件:
1 <= word.length <= 5 * 10^5word仅由字符 ‘a’、’e’、‘i’、‘o’、‘u’ 组成
解题思路
这道题目要求找到包含所有元音字母且按字母顺序排列的最长子串。关键在于理解什么是"按字母顺序排列"——这意味着子串必须遵循 a...a e...e i...i o...o u...u 的模式。
解题思路:
可以采用滑动窗口的方法来解决:
状态追踪:使用一个状态变量来追踪当前遇到的最后一个元音字母。因为必须按顺序,所以只需要知道当前到达了哪个元音。
窗口扩展策略:
- 从每个 ‘a’ 开始尝试构建美丽子串
- 对于当前字符,判断是否能继续扩展窗口
- 如果当前字符等于状态字符,可以继续
- 如果当前字符是状态字符的下一个元音,更新状态并继续
- 否则,当前子串结束
优化细节:
- 只有当状态到达 ‘u’ 时,才表示找到了一个完整的美丽子串
- 当遇到无法继续的字符时,如果该字符是 ‘a’,可以从这里开始新的尝试
- 使用一次遍历即可完成,时间复杂度为 O(n)
这种方法的核心是状态机的思想,每个状态代表当前已经匹配到的最后一个元音字母。
代码实现
class Solution {
public:
int longestBeautifulSubstring(string word) {
int n = word.length();
int maxLen = 0;
int i = 0;
while (i < n) {
if (word[i] != 'a') {
i++;
continue;
}
int start = i;
char expected = 'a';
while (i < n) {
if (word[i] < expected || word[i] > 'u') {
break;
}
if (word[i] > expected) {
if (word[i] != expected + 1) {
break;
}
expected = word[i];
}
if (expected == 'u') {
maxLen = max(maxLen, i - start + 1);
}
i++;
}
}
return maxLen;
}
};
class Solution:
def longestBeautifulSubstring(self, word: str) -> int:
max_len = 0
i = 0
n = len(word)
while i < n:
if word[i] != 'a':
i += 1
continue
start = i
expected = 'a'
while i < n:
if word[i] < expected or word[i] > 'u':
break
if word[i] > expected:
if ord(word[i]) != ord(expected) + 1:
break
expected = word[i]
if expected == 'u':
max_len = max(max_len, i - start + 1)
i += 1
return max_len
public class Solution {
public int LongestBeautifulSubstring(string word) {
int maxLen = 0;
int i = 0;
int n = word.Length;
while (i < n) {
if (word[i] != 'a') {
i++;
continue;
}
int start = i;
char expected = 'a';
while (i < n) {
if (word[i] < expected || word[i] > 'u') {
break;
}
if (word[i] > expected) {
if (word[i] != expected + 1) {
break;
}
expected = word[i];
}
if (expected == 'u') {
maxLen = Math.Max(maxLen, i - start + 1);
}
i++;
}
}
return maxLen;
}
}
/**
* @param {string} word
* @return {number}
*/
var longestBeautifulSubstring = function(word) {
let maxLen = 0;
let i = 0;
while (i < word.length) {
if (word[i] !== 'a') {
i++;
continue;
}
let start = i;
let vowelSet = new Set();
vowelSet.add('a');
while (i < word.length && word[i] === 'a') {
i++;
}
while (i < word.length && word[i] === 'e') {
vowelSet.add('e');
i++;
}
while (i < word.length && word[i] === 'i') {
vowelSet.add('i');
i++;
}
while (i < word.length && word[i] === 'o') {
vowelSet.add('o');
i++;
}
while (i < word.length && word[i] === 'u') {
vowelSet.add('u');
i++;
}
if (vowelSet.size === 5) {
maxLen = Math.max(maxLen, i - start);
}
}
return maxLen;
};
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 每个字符最多被访问两次 |
| 空间复杂度 | O(1) | 只使用常量级别的额外空间 |