字符换替换与最长字串问题 | MarsCode AI刷题

46 阅读3分钟

问题描述

有一个由大写字母组成的字符串长度为n,现在可以对字符串中的字符进行修改:
每次允许将某个位置的字符修改为任意字符,比如说将字符串ABC第1个字符A改为B,则字符串变成BBC
这个操作最多可以执行k次,现在想知道修改之后,字符串中由最多两种字母组成的子串最大长度。

输入格式

每个样例有两行;
第一行是整数 nkn 表示字符串长度,k 表示可以修改的最多次数;(3≤n≤2000, 1≤k≤100)
第二行是长度为 n 的字符串;

题目思路:根据题目,我们可以用滑动窗口的思路来解决问题,我们定义一个maxLength来返回结果,我们只需要定义一个Map集合,key用来存放字符,value用来标记字符出现的次数,根据集合的特性,如果key重复的话,我们就将key的value值+1,本问题的关键在于,我们需要知道出现最多次数的两个字符的数量,然后map集合中总的数量减去他们得到剩余的数量count,用count和k作比较,如果count不大于k,说明修改次数就是小于k的,我们可以修改maxLength的值,如果count大于k,说明修改次数已经超过了题目的要求,我们需要减少修改的次数,根据滑动窗口,我们只需要减少最左边字符的数量,然后统计count,然后作比较,最后遍历完整个字符,我们就可以得到我们想要的答案。

代码示例:

import java.util.*;

public class Main {
    public static int solution(int n, int k, String inp) {
        //将inp转换为数组,直接按照索引找字符比.charAt的速度要快
        char[] chars = inp.toCharArray();
        //用来标记maxLength是否发生变化,如果没有,说明整个字符串中不同的字符少于两个,我们直接返回字符串的长度即可
        boolean flag = false;
        int maxLength = 0;
        int left = 0;
        //用来控制循环的位置
        int right = 0;
        int count = 0;
        int total = 0;
        Map<Character, Integer> counts = new HashMap<>();
        while (right < n) {
            char rightChar = chars[right];
            //滑动窗口的精髓,滑动右边的窗口
            counts.put(rightChar, counts.getOrDefault(rightChar, 0) + 1);
            total++;
            if (counts.size() > 2) {
                count = findOtherCount(counts, total);
                if (count <= k) {
                //如果修改的次数小于k次,那么我们就修改maxLength的值
                    maxLength = Math.max(maxLength, right - left + 1);
                } else {
                    while (count> k) {
                        char leftChar = chars[left];
                        //滑动窗口的精髓,滑动左边的窗口
                        counts.put(leftChar, counts.get(leftChar) - 1);
                        total--;
                        //注意先后顺序,我这里是在减之后做判断,这样子的程序判断更加的简单,value减完之后,如果等于0,我们直接删除key就可以了。
                        if (counts.get(leftChar) == 0) {
                            counts.remove(leftChar);
                        }
                        left++;
                        //这段代码同样十分重要,放在前面和放在后面会有不同的效果
                        count = findOtherCount(counts, total);
                    }

                }
                flag = true;
            }
            //一直向右滑动窗口
            right++;
        }
        System.out.println(maxLength);
       
        if (flag) {
        //如果发生了变化,我们就返回maxLength
            return maxLength;
        } else {
         //如果maxLength没有变化,我们直接返回字符串的长度
            return chars.length;
        }
    }
//这个方法的作用是将map按照value排序,这样可以快速的找到出现次数在前2的值
//方法的关键在于将map转换为Map.Entry的类型,然后使用Collections.sort的Comparator方法来进行排序
//总的个数好计算,我们只需要在添加字符的时候加1,减少字符的时候减1就可以了
    private static int findOtherCount(Map<Character, Integer> counts, int total) {
        //先找到counts中最大的两个数
        //由大到小排序
        List<Map.Entry<Character, Integer>> list = new ArrayList<>(counts.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<Character, Integer>>() {
            @Override
            public int compare(Map.Entry<Character, Integer> o1, Map.Entry<Character, Integer> o2) {
            //由大到小排序
                return o2.getValue().compareTo(o1.getValue());
            }
        });

        return total - list.get(0).getValue() - list.get(1).getValue();
    }


    public static void main(String[] args) {
        System.out.println(solution(6, 1, "ABCBAD") == 5);
        System.out.println(solution(5, 1, "AEABD") == 4);
        System.out.println(solution(17, 6, "XUERYYMQOSONUVBZN") == 10);
        System.out.println(solution(1, 25, "J") == 1);


    }
}