最小替换子串长度
问题描述
小F得到了一个特殊的字符串,这个字符串只包含字符A、S、D、F,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得A、S、D、F这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。
测试样例
样例1:
输入:input = “ADDF”
输出:1
样例2:
输入:input = “ASAFASAFADDD”
输出:3
样例3:
输入:input = “SSDDFFFFAAAS”
输出:1
样例4:
输入:input = “AAAASSSSDDDDFFFF”
输出:0
样例5:
输入:input = “AAAADDDDAAAASSSS”
输出:4
求解
法一:
import java.util.HashMap;
import java.util.Map;
public class Main {
public static int solution(String input) {
// Please write your code here
if (input == null || input.length() == 0) {
return 0;
}
int n = input.length();
int targetCount = n / 4;
// 统计每个字符的频次
Map<Character, Integer> charCount = new HashMap<>();
for (char c : input.toCharArray()) {
charCount.put(c, charCount.getOrDefault(c, 0) + 1);
}
// 如果已经满足条件,直接返回0
if (isBalanced(charCount, targetCount)) {
return 0;
}
// 滑动窗口
int left = 0;
int minLen = n;
for (int right = 0; right < n; right++) {
char rightChar = input.charAt(right);
charCount.put(rightChar, charCount.get(rightChar) - 1);
while (isBalanced(charCount, targetCount)) {
minLen = Math.min(minLen, right - left + 1);
char leftChar = input.charAt(left);
charCount.put(leftChar, charCount.get(leftChar) + 1);
left++;
}
}
return minLen;
}
private static boolean isBalanced(Map<Character, Integer> charCount, int targetCount) {
for (int count : charCount.values()) {
if (count > targetCount) {
return false;
}
}
return true;
}
public static void main(String[] args) {
// You can add more test cases here
System.out.println(solution("ADDF") == 1);
System.out.println(solution("ASAFASAFADDD") == 3);
}
}
法二:
public class Main {
public static int solution(String input) {
int n = input.length();
int targetCount = n / 4;
// 统计每个字符的初始频次
int[] count = new int[4]; // 0: A, 1: S, 2: D, 3: F
for (char c : input.toCharArray()) {
if (c == 'A') count[0]++;
else if (c == 'S') count[1]++;
else if (c == 'D') count[2]++;
else if (c == 'F') count[3]++;
}
// 计算每个字符需要替换的次数
int[] needReplace = new int[4];
for (int i = 0; i < 4; i++) {
needReplace[i] = Math.max(0, count[i] - targetCount);
}
// 如果所有字符的频次已经相等,直接返回0
if (needReplace[0] == 0 && needReplace[1] == 0 && needReplace[2] == 0 && needReplace[3] == 0) {
return 0;
}
// 使用滑动窗口找到最小的替换子串长度
int left = 0, right = 0;
int minLen = n;
int[] windowCount = new int[4];
while (right < n) {
char rightChar = input.charAt(right);
if (rightChar == 'A') windowCount[0]++;
else if (rightChar == 'S') windowCount[1]++;
else if (rightChar == 'D') windowCount[2]++;
else if (rightChar == 'F') windowCount[3]++;
while (left <= right && windowCount[0] >= needReplace[0] && windowCount[1] >= needReplace[1] && windowCount[2] >= needReplace[2] && windowCount[3] >= needReplace[3]) {
minLen = Math.min(minLen, right - left + 1);
char leftChar = input.charAt(left);
if (leftChar == 'A') windowCount[0]--;
else if (leftChar == 'S') windowCount[1]--;
else if (leftChar == 'D') windowCount[2]--;
else if (leftChar == 'F') windowCount[3]--;
left++;
}
right++;
}
return minLen;
}
public static void main(String[] args) {
// 你可以添加更多测试用例
System.out.println(solution("ADDF") == 1);
System.out.println(solution("ASAFASAFADDD") == 3);
System.out.println(solution("SSDDFFFFAAAS") == 1);
System.out.println(solution("AAAASSSSDDDDFFFF") == 0);
System.out.println(solution("AAAADDDDAAAASSSS") == 4);
}
}
滑动窗口部分的代码
// 滑动窗口
int left = 0; // 初始化左指针
int minLen = n; // 初始化最小长度为字符串的总长度
for (int right = 0; right < n; right++) { // 右指针从0开始遍历到字符串末尾
char rightChar = input.charAt(right); // 获取右指针指向的字符
charCount.put(rightChar, charCount.get(rightChar) - 1); // 减少该字符的频次
// 当前窗口内的字符频次满足条件时
while (isBalanced(charCount, targetCount)) {
minLen = Math.min(minLen, right - left + 1); // 更新最小长度
char leftChar = input.charAt(left); // 获取左指针指向的字符
charCount.put(leftChar, charCount.get(leftChar) + 1); // 增加该字符的频次
left++; // 移动左指针,缩小窗口
}
}
详细解释
初始化指针和变量
int left = 0; // 初始化左指针
int minLen = n; // 初始化最小长度为字符串的总长度
left:左指针,初始值为0,表示窗口的起始位置。
minLen:最小长度,初始值为字符串的总长度,表示当前找到的最小子串的长度。右指针遍历字符串
for (int right = 0; right < n; right++) { // 右指针从0开始遍历到字符串末尾
char rightChar = input.charAt(right); // 获取右指针指向的字符
charCount.put(rightChar, charCount.get(rightChar) - 1); // 减少该字符的频次
right:右指针,从0开始遍历到字符串的末尾。
rightChar:右指针指向的字符。
charCount.put(rightChar, charCount.get(rightChar) - 1):减少右指针指向的字符的频次。这是因为我们假设这个字符已经被替换掉了,所以它的频次减少。
检查窗口内的字符频次是否满足条件
while (isBalanced(charCount, targetCount)) {
minLen = Math.min(minLen, right - left + 1); // 更新最小长度
char leftChar = input.charAt(left); // 获取左指针指向的字符
charCount.put(leftChar, charCount.get(leftChar) + 1); // 增加该字符的频次
left++; // 移动左指针,缩小窗口
}
while (isBalanced(charCount, targetCount)):
调用 isBalanced 方法检查当前窗口内的字符频次是否满足条件(即所有字符的频次都小于等于目标频次)。
minLen = Math.min(minLen, right - left + 1):
如果当前窗口内的字符频次满足条件,更新最小长度。right - left + 1 表示当前窗口的长度。
char leftChar = input.charAt(left):
获取左指针指向的字符。
charCount.put(leftChar, charCount.get(leftChar) + 1):增加左指针指向的字符的频次,因为我们要缩小窗口,所以恢复这个字符的频次。
left++:移动左指针,缩小窗口。
示例解释
假设输入字符串为 “ASAFASAFADDD”,目标频次为 targetCount = 3。
初始化
left = 0
minLen = 12
charCount = {‘A’: 4, ‘S’: 4, ‘F’: 2, ‘D’: 2}
右指针遍历
right = 0,rightChar = ‘A’,charCount = {‘A’: 3, ‘S’: 4, ‘F’: 2, ‘D’: 2}
right = 1,rightChar = ‘S’,charCount = {‘A’: 3, ‘S’: 3, ‘F’: 2, ‘D’: 2}
right = 2,rightChar = ‘A’,charCount = {‘A’: 2, ‘S’: 3, ‘F’: 2, ‘D’: 2}
right = 3,rightChar = ‘F’,charCount = {‘A’: 2, ‘S’: 3, ‘F’: 1, ‘D’: 2}
检查平衡条件
right = 3,charCount 满足平衡条件,minLen = 4
left = 0,leftChar = ‘A’,charCount = {‘A’: 3, ‘S’: 3, ‘F’: 1, ‘D’: 2}
left = 1,leftChar = ‘S’,charCount = {‘A’: 3, ‘S’: 4, ‘F’: 1, ‘D’: 2}
right = 4,rightChar = ‘A’,charCount = {‘A’: 2, ‘S’: 4, ‘F’: 1, ‘D’: 2}
right = 5,rightChar = ‘S’,charCount = {‘A’: 2, ‘S’: 3, ‘F’: 1, ‘D’: 2}
right = 6,rightChar = ‘A’,charCount = {‘A’: 1, ‘S’: 3, ‘F’: 1, ‘D’: 2}
right = 7,rightChar = ‘F’,charCount = {‘A’: 1, ‘S’: 3, ‘F’: 0, ‘D’: 2}
继续检查平衡条件
right = 7,charCount 满足平衡条件,minLen = 8
left = 2,leftChar = ‘A’,charCount = {‘A’: 2, ‘S’: 3, ‘F’: 0, ‘D’: 2}
left = 3,leftChar = ‘F’,charCount = {‘A’: 2, ‘S’: 3, ‘F’: 1, ‘D’: 2}
left = 4,leftChar = ‘A’,charCount = {‘A’: 3, ‘S’: 3, ‘F’: 1, ‘D’: 2}
left = 5,leftChar = ‘S’,charCount = {‘A’: 3, ‘S’: 4, ‘F’: 1, ‘D’: 2}
right = 8,rightChar = ‘A’,charCount = {‘A’: 2, ‘S’: 4, ‘F’: 1, ‘D’: 2}
right = 9,rightChar = ‘D’,charCount = {‘A’: 2, ‘S’: 4, ‘F’: 1, ‘D’: 1}
right = 10,rightChar = ‘D’,charCount = {‘A’: 2, ‘S’: 4, ‘F’: 1, ‘D’: 0}
right = 11,rightChar = ‘D’,charCount = {‘A’: 2, ‘S’: 4, ‘F’: 1, ‘D’: -1}
最终结果
minLen = 3
通过这个过程,滑动窗口不断地调整大小,最终找到了满足条件的最小子串长度。