字节青训营11.3每日一题| 豆包MarsCode AI刷题

72 阅读5分钟

最小替换子串长度

问题描述

小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 通过这个过程,滑动窗口不断地调整大小,最终找到了满足条件的最小子串长度。