最长的最美好子字符串
思路:
滑动窗口机制: 首先窗口的左右端都指向初始位置,遍历字符串进行滑动. 我们要得到的就是一个最长的窗口,里面所有字符种类同时包含每种字符的大小写.
这里窗口中的字符种类最小为1,最大为字符集的种类数max.因此我们可以设置窗口字符种类数从1到max,在此期间检查窗口滑动过程中能得到的最大最美好字符串并记录出现的索引及长度.
比如AaaAbB
设置字符种类为typeNum = 1,能得到的最长最美好字符串为AaaA
设置字符种类为typeNum = 2,能得到的最长最美好字符串为AaaAbB
代码
class Solution {
private int maxPos;
private int maxLen;
public String longestNiceSubstring(String s) {
this.maxPos = 0;
this.maxLen = 0;
int types = 0;
for(int i = 0;i < s.length();i++){
types |= 1 << (Character.toLowerCase(s.charAt(i)) - 'a');
}
// 得到字符串中的字符的种类数量
types = Integer.bitCount(types);
for(int i = 1;i <= types;i++){
check(s,i);
}
return s.substring(maxPos, maxPos + maxLen);
}
//在窗口中,维护一个记录大小写字母同时出现的字符种类数cnt和不同种类字符出现的次数total
public void check(String s, int typeNum){
int[] lowerCnt = new int[26];
int[] upperCnt = new int[26];
int cnt = 0;
// 开始时,左右边界都指向0,cnt,total都初始化为0
for(int l = 0,r = 0,total = 0;r < s.length();r++){
int idx = Character.toLowerCase(s.charAt(r)) - 'a';
//如果遍历到某个字符时,为大小写字母同时出现则cnt++
if(Character.isLowerCase(s.charAt(r))){
lowerCnt[idx]++;
if(lowerCnt[idx] == 1 && upperCnt[idx] > 0){
cnt++;
}
}else{
upperCnt[idx]++;
if(upperCnt[idx] == 1 && lowerCnt[idx] > 0){
cnt++;
}
}
//如果遍历到某个字符时,为一种新的字符集,total++
total += (lowerCnt[idx] + upperCnt[idx]) == 1 ? 1 : 0;
//当total > typeNum时,滑动窗口左端向右移动
while(total > typeNum){
idx = Character.toLowerCase(s.charAt(l)) - 'a';
total -= (lowerCnt[idx] + upperCnt[idx]) == 1 ? 1 : 0;
if(Character.isLowerCase(s.charAt(l))){
lowerCnt[idx]--;
if(lowerCnt[idx] == 0 && upperCnt[idx] > 0){
cnt--;
}
}else{
upperCnt[idx]--;
if(upperCnt[idx] == 0 && lowerCnt[idx] > 0){
cnt--;
}
}
l++;
}
//在滑动过程中更新maxPos和maxLen
if(cnt == typeNum && maxLen < r - l + 1){
maxPos = l;
maxLen = r - l + 1;
}
}
}
}