滑动窗口
3. 无重复字符的最长子串
class Solution {
public int lengthOfLongestSubstring(String s) {
int res = 0;
int l = 0;
int r = 0;
Map<Character, Integer> map = new HashMap<>(); // 记录字符出现个数,始终要维持 1
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
r++; // 无脑向前
char temp = chars[i];
if (map.getOrDefault(temp, 0) > 0) {
for (int j = l; j < i; j++) {
if (chars[j] == temp) { // 找到了前面要加入的
l++;
break;
} else {
l++;
map.put(chars[j], 0); // 清空前方字符
}
}
}
map.put(temp, 1); // 新加的数,放入 map 中
res = Math.max(res, r - l); // r 是从 1 开始的(当前数的下一个位置)
}
return res;
}
}
76. 最小覆盖子串
粗略实现
class Solution {
Map<Character, Integer> targetMap = new HashMap<>();
Map<Character, Integer> map = new HashMap<>();
// 后面返回的是:start 和 len
public String minWindow(String s, String t) {
boolean flag = false; // 能够找到
int start = 0;
int Len = s.length(); // 子串初始长度
int left = 0;
int right = 0;
// 只有当满足条件时吗,才考虑精简
for (char c : t.toCharArray()) {
targetMap.put(c, targetMap.getOrDefault(c, 0) + 1);
}
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
right++;
char temp = chars[i]; // 待考察的
if (targetMap.containsKey(temp)) { // 有用的就要
map.put(temp, map.getOrDefault(temp, 0) + 1);
if (isFull()) { // 加完之后已经满了
flag = true;
for (int j = left; j <= i; j++) { // 这里要有等号(考虑到:target 只有一个的情况【ab a】)
if (targetMap.containsKey(chars[j])) {
map.put(chars[j], map.get(chars[j]) - 1); // 逐个干掉!!!
if (!isFull()) { // 干掉之后已经不满了
if (Len > right - left) {
Len = right - left;
start = left;
}
left++;
break;
}else {
left++;
}
}else { // 无关紧要的,向右推进
left++;
}
}
}
}
}
if (flag) {
return s.substring(start, start + Len);
}
return "";
}
public boolean isFull(){
for (Character character : targetMap.keySet()) {
if (targetMap.get(character).compareTo(map.getOrDefault(character, -1)) > 0){
return false;
}
}
return true;
}
}
由于都是英文字母 ===> 使用数组进行优化
class Solution {
int[] targetMap = new int[128];
int[] map = new int[128];
// 后面返回的是:start 和 len
public String minWindow(String s, String t) {
boolean flag = false; // 能够找到
int start = 0;
int Len = s.length(); // 子串初始长度
int left = 0;
int right = 0;
// 只有当满足条件时吗,才考虑精简
for (char c : t.toCharArray()) {
targetMap[c]++;
}
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
right++;
char temp = chars[i]; // 待考察的
if (targetMap[temp] != 0) { // 有用的就要
map[temp]++;
if (isFull()) { // 加完之后已经满了
flag = true;
// 这里主要还是因为我们要求的是最小值!!!(所以考虑 Len 为 1 的情况)
for (int j = left; j <= i; j++) { // 这里要有等号(考虑到:target 只有一个的情况【ab a】)
if (targetMap[chars[j]] != 0) {
map[chars[j]]--; // 把这个干掉
if (!isFull()) { // 干掉之后已经不满了
if (Len > right - left) {
Len = right - left;
start = left;
}
left++;
break;
}else {
left++;
}
}else { // 无关紧要的,向右推进
left++;
}
}
}
}
}
if (flag) {
return s.substring(start, start + Len);
}
return "";
}
public boolean isFull(){
for (int i = 0; i < 128; i++) {
if (map[i] < targetMap[i]){
return false;
}
}
return true;
}
}
变形题【关联子串】
import java.util.Scanner;
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
int start = 0;
int Len = Integer.MAX_VALUE;
int[] targetMap = new int[128];
int[] map = new int[128];
Scanner in = new Scanner(System.in);
String[] split = in.nextLine().split(" ");
String target = split[0];
String str = split[1];
for (char c : target.toCharArray()) {
targetMap[c]++;
}
int res = -1;
int left = 0;
int right = 0;
for (int i = 0; i < str.length(); i++) {
right++;
char temp = str.charAt(i);
if (targetMap[temp] != 0) { // 有用的
map[temp]++;
if (isFull(map, targetMap)) { // 现在满了,开始向前清算 ---> 干掉多余的
for (int j = left; j <= i; j++) {
char c = str.charAt(j);
if (targetMap[c] != 0) { // 有用的
map[c]--; // 将这个有用的干掉
if (!isFull(map, targetMap)) {
if (Len > right - left) { // right - left 最小值就是 target.length()
Len = right - left;
start = left; // 所以就只更新一次
}
left++;
break;
}else {
left++;
}
} else {
left++;
}
}
}
}
}
if (Len != target.length()){
System.out.println(-1);
return;
}
System.out.println(start);
}
public static boolean isFull(int[] map, int[] targetMap){
for (int i = 0; i < 128; i++) {
if (map[i] < targetMap[i]){
return false;
}
}
return true;
}
}
438. 找到字符串中所有字母异位词
class Solution {
int[] now = new int[128];
int[] target = new int[128];
public List<Integer> findAnagrams(String s, String p) {
for (char c : p.toCharArray()) {
target[c-'a']++;
}
List<Integer> list = new ArrayList<>();
int l = 0;
int r = 0;
for (int i = 0; i < s.length(); i++) {
r++;
now[s.charAt(i) - 'a']++; // 维护一个滑动窗口!!!
if (r - l < p.length()){
continue;
}
// 长度够了现在
if (isSame()) {
list.add(l);
}
now[s.charAt(l) - 'a']--; // 减去头
l++;
}
return list;
}
public boolean isSame(){
for (int i = 0; i < 128; i++) {
if (now[i] != target[i]){
return false;
}
}
return true;
}
}