11.3每日一练

最小替换子串长度

问题描述

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值