双指针解法的应用:
1.最小覆盖字串
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
思考:通过left right两个指针表明当前已遍历的字符区间,首先通过right指针找到符合条件的子串,然后通过left压缩区间,从而找到最小区间,当left收缩区间使得区间不符合条件时,right会继续往前寻找符合条件的区间,直到right遍历完成。
使用maps来记录当前遍历的区间
使用mapt来记录符合条件的区间
通过比较maps和mapt来判断是否完成区间覆盖。
使用maps保存遍历区间
class Solution {
public String minWindow(String s, String t) {
int left = 0, right = 0, min = Integer.MAX_VALUE, start = 0, valid = 0;
Map<Character, Integer> maps = new HashMap<>();
Map<Character, Integer> mapt = new HashMap<>();
for (int i = 0; i < t.length(); i++) {
mapt.put(t.charAt(i), mapt.getOrDefault(t.charAt(i), 0) + 1);
}
while (right < s.length()) {
Character c = s.charAt(right);
right++;
maps.put(c, maps.getOrDefault(c, 0) + 1);
if (mapt.containsKey(c)) {
if (maps.get(c).equals(mapt.get(c))) {
valid++;
}
}
while (valid == mapt.size()) {
if (right - left < min) {
min = right - left;
start = left;
}
Character c1 = s.charAt(left);
left++;
if (mapt.containsKey(c1)) {
if (maps.get(c1).equals(mapt.get(c1))) {
valid--;
}
maps.put(c1, maps.get(c1) - 1);
}
}
}
return min == Integer.MAX_VALUE ? "" : s.substring(start, start + min);
}
}
2.无重复字符的最长子串
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
思考:通过left和当前遍历的字符表明目前所包含的区间,将区间对应的下标通过map记录,每遍历一个新的字符,判断字符是否存在map中,若存在获取对应的下标,如果对应的下标超过left则更新left的值,并更新当前的最长子串。
若不存在map中, 更新当前最长子串的长度。
class Solution {
public int lengthOfLongestSubstring(String s) {
int left = 0, max = 0;
Map<Character, Integer> map = new HashMap<Character, Integer>();
for (int i = 0; i < s.length(); i++) {
int l = map.getOrDefault(s.charAt(i), -1);
left = Math.max(left, l);
max = Math.max(max, i - left);
map.put(s.charAt(i), i);
}
return max;
}
}
3.至多包含两个不同字符的最长字串
思考:
// Java实现
class Solution {
public int lengthOfLongestSubstringTwoDistinct(String s) {
int strLength = s.length();
if (strLength < 3) return strLength;
int left = 0;
int right = 0;
Map<Character, Integer> map = new HashMap<>();
int maxLen = 2;
while (right < strLength) {
map.put(s.charAt(right), right);
right++;
if (map.size() == 3) {
int delIdx = Collections.min(map.values());
map.remove(s.charAt(delIdx));
left = delIdx + 1;
}
maxLen = Math.max(maxLen, right - left);
}
return maxLen;
}
}
4.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0
思考:
通过定义start end两个指针表明当前遍历区间,首先通过增大end来找到符合要求的区间,由于数据都是正整数,可以通过增加start来减少区间找到符合要求的最小区间。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
if (nums.length == 0) {
return 0;
}
int start = 0, end = 0, min = Integer.MAX_VALUE;
int sum = 0;
while (end < nums.length) {
sum += nums[end++];
if (sum >= target) {
while (sum >= target) {
sum -= nums[start];
start++;
}
min = Math.min(min, end - start + 1);
}
}
return min == Integer.MAX_VALUE ? 0 :min;
}
}
最长公共子串
s1 ="testhello",s2="eehello" 求s1和s2的最长子串
//思路,创建dp数组,dp[i][j]表示s1的前i个字符,和s2的前j个字符
public int test(String test1, String test2) {
int max = 0;
int m = test1.length();
int n = test2.length();
int[][] dp = new int[test1.length() + 1][test2.length() + 1];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (test1.charAt(i - 1) == test2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
max = Math.max(max, dp[i][j]);
}
}
}
return max;
}