当一个字符串 s 包含的每一种字母的大写和小写形式 同时 出现在 s 中,就称这个字符串 s 是 美好 字符串。比方说,“abABB” 是美好字符串,因为 ‘A’ 和 ‘a’ 同时出现了,且 ‘B’ 和 ‘b’ 也同时出现了。然而,“abA” 不是美好字符串因为 ‘b’ 出现了,而 ‘B’ 没有出现。
给你一个字符串 s ,请你返回 s 最长的 美好子字符串 。如果有多个答案,请你返回 最早 出现的一个。如果不存在美好子字符串,请你返回一个空字符串。
示例 1:
输入:s = “YazaAay”
输出:“aAa”
解释:“aAa” 是一个美好字符串,因为这个子串中仅含一种字母,其小写形式 ‘a’ 和大写形式 ‘A’ 也同时出现了。
“aAa” 是最长的美好子字符串。
示例 2:
输入:s = “Bb”
输出:“Bb”
解释:“Bb” 是美好字符串,因为 ‘B’ 和 ‘b’ 都出现了。整个字符串也是原字符串的子字符串。
示例 3:
输入:s = “c”
输出:""
解释:没有美好子字符串。
示例 4:
输入:s = “dDzeE”
输出:“dD”
解释:“dD” 和 “eE” 都是最长美好子字符串。
由于有多个美好子字符串,返回 “dD” ,因为它出现得最早。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-nice-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
class Solution {
private:
int maxPos;
int maxLen;
public:
void dfs(const string & s, int start, int end) {
if (start >= end) return;
int lower = 0, upper = 0;
for (int i = start; i <= end; i++) {
if (islower(s[i])) {
lower |= 1 << (s[i] - 'a');
}
else {
upper |= 1 << (s[i] - 'A');
}
}
if (lower == upper) {
int L = end - start + 1;
if (L > maxLen) {
maxPos = start;
maxLen = L;
}
return;
}
int valid = lower & upper;
int pos = start;
while (pos < end) {
start = pos;
while (pos <= end && valid &(1 << (tolower(s[pos]) - 'a'))) {
pos++;
}
dfs(s, start, pos - 1);
pos++;
}
}
string longestNiceSubstring(string s) {
maxLen = 0;
dfs(s, 0, s.size() - 1);
return s.substr(maxPos, maxLen);
}
};
解释:
任何包含不能大小写配对字符的子串都不能是一个美好字符串,所以使用分治的思想,当遇到一个这样的字符就判断这个字符左边的子串和右边的子串是否是美好字符串,如果是美好字符串并且长度大于当前的最长美好字符串,就更新最大字符串。
时间复杂度O(n * s)n是字符串长度,s是字母表长度,即大小写总共52。因为dfs递归时每次都会排除一个大小写不配对的字符,所以递归的深度最大为26,每次递归时需要遍历当前从start到end的子串,所以一次递归复杂度为O(n),总共O(n s)
空间复杂度 O(s)递归栈空间,同递归深度
TIPS
1. C++中判断一个字符是否为小写字母可使用islower()函数
2. C++中大写转小写可以用tolower()函数
3. 用int变量记录字母是否出现过的方法: lower |= 1 << (s[i] - 'a'), 1根据字母表的位置移位,与变量取位或