问题描述
有一个由大写字母组成的字符串长度为n,现在可以对字符串中的字符进行修改:
每次允许将某个位置的字符修改为任意字符,比如说将字符串ABC第1个字符A改为B,则字符串变成BBC;
这个操作最多可以执行k次,现在想知道修改之后,字符串中由最多两种字母组成的子串最大长度。
输入格式
每个样例有两行;
第一行是整数 n, k,n 表示字符串长度,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);
}
}