包含相同个数‘A’,‘B’,‘C’字符的最长子串
2023-04-17 21:15:52 # 算法 # 问题原型

给出一个字符串,只包含A,B,C三种字符,现在需要找到一个最长的子串,包含A,B,C的个数一样多。返回这样的最长子串的长度。

1. 两层遍历

这个方法比较好想,既然我们是要找到一个子串,我们可以枚举这个子串的开头位置。对于任何一个开头,我们往后找可能的末尾位置。遍历的同时使用变量记录各个字符的个数,然后检查字符出现的个数是否一样。记录下最长的满足条件的字符串长度即可。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LongestSubStrContainsEquABC {

public static int maxLen(String s) {
int len;
if(s == null || (len = s.length()) < 3) return 0;
int ans = 0, a = 0, b = 0, c = 0, i = 0, j;
char cc;
for(; i < len; ++i) {
a = 0;
b = 0;
c = 0;
for(j = i; j < len; ++j) {
cc = s.charAt(j);
if(cc == 'A') ++a;
else if(cc == 'B') ++b;
else ++c;
if(a == b && a == c) ans = Math.max(ans, j - i + 1);
}
}
return ans;
}
}

2. 使用哈希表

如果理解平移操作,就很容易理解这个方法。对于一个图形进行平移后,图形所有对应点之间的距离是相等的,如下图所示:

我们不妨把字符A,B,C出现的次数放在条形图中,记录条形图顶端形成的形状。如下图所示:

我们可以遍历字符串,统计字符ABC出现的次数,在统计的同时,我们需要查看在之前的哪一步出现了统计图顶端的图形。假设当我们遍历到第$j$个字符的时候,发现相同的图形出现在遍历第$i$个字符的时候,那么我们可以知道从$i+1$到$j$这一段子串包含的ABC字符个数必然相同。那么我们怎么记录统计图顶端形成的图形呢?其实这个图形的本质就是ABC各个字符出现次数的差异,我们只需要记录下差异,就相当于记录下了图形。我们在遍历的时候,用哈希表记录下遍历到每一步的时候,此时统计图形成的图形,以便后续遍历的时候查找。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.HashMap;

public class LongestSubStrContainsEquABC2 {

public static int maxLen(String s) {
int len;
if(s == null || (len = s.length()) < 3) return 0;
HashMap<String, Integer> diffShapeMap = new HashMap<>();
diffShapeMap.put("0-0", -1);
int a = 0, b = 0, c = 0, ans = 0;
char cc;
for(int i = 0; i < len; ++i) {
cc = s.charAt(i);
if(cc == 'A') ++a;
else if(cc == 'B') ++b;
else ++c;
String diffShape = (b - a) + "-" + (c - b);
if(diffShapeMap.containsKey(diffShape)) ans = Math.max(ans, i - diffShapeMap.get(diffShape));
else diffShapeMap.put(diffShape, i);
}
return ans;
}
}