340.至多包含 K 个不同字符的最长子串
思路
寻找字符串满足某个条件的子串,考虑双指针+滑动窗口思想(右指针一直前进,当遇到某个条件成立/不成立,更新左指针,然后右指针接着前进)。首先左右指针从0开始,右指针步进,用hashmap存储俩指针区间内每个字符最新出现的下标,当hashmap中元素个数大于要求的k时,证明元素多了,上一个子串寻找结束,更新左指针开始下一个子串寻找(舍弃目前区间内最右下标最小的舍弃,以获取最大长度),直到到最后。
代码
public class T340 {
public int lengthOfLongestSubstringKDistinct(String s, int k) {
//滑动窗口:右指针指向当前处理位置,左指针根据条件步进
//字符串寻找各种类型的子串 均可以考虑是否可以滑动窗口,右指针一直前进,当遇到某个条件成立/不成立,更新左指针,然后右指针接着前进。
//此题需要寻找字符串至多包含k个字符的最长子串,返回长度即可。
//需要记录目前最长的,步进中不断更新,每次新的左指针变化前更新。
//字符计数,最好用的还是hashmap,而此题每次更新串都是舍弃最早结束的字符。
if (k==0||s.length()==0) return 0;
int l=0,r=0;
int len=0;
Map<Character,Integer> map=new HashMap<>();
while (r<s.length()){
//先处理目前的r上的元素
map.put(s.charAt(r),r);
//当满足某个条件,需要暂时停下处理l指针
if (map.size()>k){
int min=Collections.min(map.values());
map.remove(s.charAt(min));
l=min+1;
}
//正常步进
r++;
//更新指标
len=len>r-l?len:r-l;
}
return len;
}
}
技巧
- 滑动窗口:右指针指向当前处理位置,当满足某个条件再步进左指针。书写代码时遵循,①先处理当前右指针元素,②然后如果满足某个条件,进行处理,左指针步进,③右指针步进,④更新指标。并且外层循环一般都是
while (r<s.length())
。 - 字符计数,最好用的还是hashmap。虽然有时可以用128大小的int数组,下标表示ASCII码。
Map<Character,Integer> map=new HashMap<>();
:
① HashMap是Map接口的主要实现类
② 使用哈希算法对键去重复,效率高,但无序
③ 提供key-value的存储和查询功能
④- Java Collections类操作:
Collections.min(map.values());
①reverse(List list)
:对指定 List 集合元素进行逆向排序。
sort(List list)
:根据元素的自然顺序对指定 List 集合的元素按升序进行排序。
②Object max(Collection coll)
:根据元素的自然顺序,返回给定集合中的最大元素。
Object min(Collection coll)
:根据元素的自然顺序,返回给定集合中的最小元素。
Object max(Collection coll, Comparator comp)
:根据 Comparator 指定的顺序,返回给定集合中的最大元素。
int frequency(Collection c, Object o)
:返回指定集合中指定元素的出现次数。
int indexOfSubList(List source, List target)
:返回子 List 对象在父 List 对象中第一次出现的位置索引;如果父 List 中没有出现这样的子 List,则返回 -1。
int lastIndexOfSubList(List source, List target)
:返回子 List 对象在父 List 对象中最后一次出现的位置索引;如果父 List 中没有岀现这样的子 List,则返回 -1。