题目链接:https://leetcode.com/problems/longest-substring-with-at-least-k-repeating-characters/
题目:
T of a given string (consists of lowercase letters only) such that every character in T appears no less than k
Example 1:
Input: s = "aaabb", k = 3 Output: 3 The longest substring is "aaa", as 'a' is repeated 3 times.
Example 2:
Input: s = "ababbc", k = 2 Output: 5 The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times.
思路:
1.先从头开始扫描,遇到第一个满足条件的子串,则用set 记录该子串的字符种类,并记录该子串的长度。此时字符串已经被分割成了一段一段的,设某段结尾字符的下标为 i ,则sets[i]表示以i为结尾的子串的字符集,len[i]表示该串的长度。此时set数组的每个set元素有三种状态,一个为null 表示该字符处于某有效字符串中,set长度为0表示从该字符开始的子串没有符合条件的,所以set为0,可以当成分隔符号,set长度大于0,则以当前字符为结尾的存在有效子串。
2. 下一步考虑合并这些一段一段的子串或分隔符,合并的时候不仅需要知道前子串的字符集 还要知道前段子串的长度 所以我设置了set、len数组。 扫描set数组,若当前set为0 则判断该字符是否和前一段构成子串;若当前set不为0 则将该子串和前一段子串或分隔符直接连接起来。
时间开销主要在分割子串上,时间复杂度为O(n^2),空间复杂度为O(n)。代码可以更进一步简化。
算法:
public int longestSubstring(String s, int k) {
if (s.length() < k)
return 0;
int max = 0;
HashSet[] sets = new HashSet[s.length()];
int len[] = new int[s.length()];// 子集合的长度
// 有三种状态:为null表示当前字符在某个符合条件的子串中;
// size为0的set表示当前字符不能和任何之后的子串表示一个符合要求的子串
// size不为0,表示之前某字符开始到当前字符为止的字符串满足条件
// 分割
for (int i = 0; i < s.length(); i++) {
int map[] = new int[26];
boolean flag2 = false;// 从i开始,是否有长度大于2的子串满足条件
for (int j = i; j < s.length(); j++) {
map[s.charAt(j) - 'a']++;
boolean flag = true;// i~j段是否满足条件
for (int m : map) {
if (m > 0 && m < k) {
flag = false;
}
}
if (flag) {// 若i~j子串符合条件,则将子串字符加入set中,并记录该子串的长度
sets[j] = new HashSet<Character>();
for (int kk = 0; kk < map.length; kk++) {
if (map[kk] != 0) {
sets[j].add((char) (kk + 'a'));
len[j] += map[kk];
}
}
i = j;
flag2 = true;// 有子串满足条件
break;
}
}
if (flag2 == false) {// 当前没有满足条件的子串,第i字符定为分隔符(i~s.length为止没有子串符合条件)
sets[i] = new HashSet<Character>();
}
}
int pre = 0;// 找到第一个子串或分割字符
for (int i = 0; i < s.length(); i++) {
if (sets[i] != null) {
pre = i;
break;
}
}
// 合并
for (int i = pre + 1; i < s.length(); i++) {
if (sets[i] != null) {
if (sets[i].size() == 0 && sets[pre].size() != 0 && sets[pre].contains(s.charAt(i))) {
sets[i].addAll(sets[pre]);
len[i] = len[pre] + 1;
} else if (sets[i].size() != 0 && sets[pre].size() != 0) {
sets[i].addAll(sets[pre]);
len[i] += len[pre];
}
pre = i;
}
}
for (int i = 0; i < s.length(); i++) {
max = Math.max(max, len[i]);
// System.out.println(sets[i] == null ? "null" : sets[i].size() +
// ":" + len[i]);
}
return max;
}