1. 最长子串问题
(1)无重复字符的最长子串
思路:窗口内为无重复字符子串,使用哈希表来保存最新碰到的字符下标,只要出现重复就缩小left指针,缩小到无重复字符也就是map.get(c)+1
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character,Integer> map = new HashMap<Character,Integer>();
int l = 0;
int r = 0;
int len = s.length();
int res = 0;
while(r<len){
char c = s.charAt(r);
if(map.get(c) != null){
l = Math.max(l,map.get(c) + 1);
}
map.put(c,r);
res = Math.max(r-l+1,res);
r++;
}
return res;
}
}
(2)至多包含两个不同字符的最长子串
思路:窗口为两个以内的不同字符,使用哈希表来保存最新碰到的字符下标,和上一题不同的是这里需要关注哈希表的size,如果超过2代表窗口内有两个以上不同的字符了,需要更新left指针位置为记录的窗口内最小的位置,并移除
public static int lengthOfLongestSubstringTwoDistinct(String s) {
if (s.length() < 3) {
return s.length();
}
int left = 0, right = 0;
HashMap<Character, Integer> hashmap = new HashMap<>();
int maxLen = 2;
while (right < s.length()) {
if (hashmap.size() < 3)
hashmap.put(s.charAt(right), right++);
// 如果大小达到了3个
if (hashmap.size() == 3) {
// 最左侧要删除的位置
int del_idx = Collections.min(hashmap.values());
hashmap.remove(s.charAt(del_idx));
// 窗口left的新位置
left = del_idx + 1;
}
maxLen = Math.max(maxLen, right - left);
}
return maxLen;
}
(3)至多包含k个不同字符的最长子串
题目:给定一个字符串 s,找出 至多 包含 k 个不同字符的最长子串T
思路:只需要将上一题的3改成k,完美移植
public static int lengthOfLongestSubstringKDistinct(String s, int k) {
if (s.length() < k + 1) {
return s.length();
}
int left = 0, right = 0;
HashMap<Character, Integer> hashmap = new HashMap<>();
int maxLen = k;
while (right < s.length()) {
if (hashmap.size() < k + 1)
hashmap.put(s.charAt(right), right++);
// 如果大小达到了k个
if (hashmap.size() == k + 1) {
//
int del_idx = Collections.min(hashmap.values());
hashmap.remove(s.charAt(del_idx));
// 窗口left的新位置
left = del_idx + 1;
}
maxLen = Math.max(maxLen, right - left);
}
return maxLen;
}
2. 长度最小的子数组
思路:当小于target时就不断扩大窗口,当大于等于target的时候就从l开始不断剔除元素
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int l = 0;
int r = 0;
int sum = 0;
int min = Integer.MAX_VALUE;
while(r<nums.length){
sum += nums[r];
while(sum>=target){
min = Math.min(min,r-l+1);
sum -= nums[l++];
}
r++;
}
return min!=Integer.MAX_VALUE ? min : 0;
}
}
3.盛水最多的容器
思路:这题和上面不同的地方在于,不是双指针从头开始慢慢增大窗口,而是从首尾开始缩小窗口
长板不动,因为长板往内移动是一定会变小容量的
短板内移,可能变大也可能变小还可能不变,遍历一遍取最大
class Solution {
public int maxArea(int[] height) {
int left = 0;
int right = height.length-1;
int low = Math.min(height[left],height[right]);
int ability = 0;
while(right > left){
if(height[left]<height[right]){
ability = Math.max(ability,(right-left)*height[left]);
left++;
}else{
ability = Math.max(ability,(right-left)*height[right]);
right--;
}
}
return ability;
}
}
4.寻找子串异位词
1)字符串的排列
思路:窗口为子串大小,从s1的位置为初始窗口位置开始遍历,看看s2窗口内是不是与s1相等的
class Solution {
public boolean checkInclusion(String s1, String s2) {
if(s1.length()>s2.length()){
return false;
}
char[] c1 = new char[26];
char[] c2 = new char[26];
int left = 0;
int right = s1.length();
for(int i = 0;i<s1.length();i++){
c1[s1.charAt(i)-'a']++;
c2[s2.charAt(i)-'a']++;
}
if(Arrays.equals(c2,c1)){
return true;
}
for(;right<s2.length();right++){
c2[s2.charAt(right)-'a']++;
c2[s2.charAt(left)-'a']--;
left++;
if(Arrays.equals(c2,c1)){
return true;
}
}
return false;
}
}
2)找到字符串中所有字母异位词
思路:和上一题差不多,窗口为子串大小,从子串大小位置为初始窗口位置开始遍历
class Solution {
public List<Integer> findAnagrams(String s, String p) {
if(s.length()<p.length()){
return new ArrayList<>();
}
char[] c1 = new char[26];
char[] c2 = new char[26];
int left = 0;
int right = p.length();
for(int i = 0;i<p.length();i++){
c1[p.charAt(i)-'a']++;
c2[s.charAt(i)-'a']++;
}
List<Integer> list = new ArrayList<>();
if(Arrays.equals(c2,c1)){
list.add(0);
}
while(right<s.length()){
c2[s.charAt(right)-'a']++;
c2[s.charAt(left)-'a']--;
left++;
if(Arrays.equals(c2,c1)){
list.add(left);
}
right++;
}
return list;
}
}