一 - 前言
介绍:大家好啊,我是hitzaki辰。
社区:(完全免费、欢迎加入)日常打卡、学习交流、资源共享的知识星球。
自媒体:我会在b站/抖音更新视频讲解 或 一些纯技术外的分享,账号同名:hitzaki辰。
正文开始,抓紧上车!
二 - 异位词问题概述
1 - 异位词是什么
1)比如 abc 和 bca就是一个异构词
2)异构词的简单判断:
(1)长度相等
(2)每个字母的数量都相等
2 - 判断异位词
对于需要比较的字符串s和t,使用哈希表可以很方便的判断异位词,维护1个count和一个哈希表。
1)关键代码1解读 - 迭代s串:
对字符串s的所有字符都进行哈希,此时map对应每个字符的数量,count代表字符的种类。
Map<Character, Integer> map = new HashMap<>(); int count = 0; for(int i=0; i<p.length(); i++){Integer temp = map.get(p.charAt(i));map.put(p.charAt(i), temp==null? 1: temp+1);if(temp==null) count++; }
比如aabc对应的map和count迭代过程为:
2)关键代码2解读 - 迭代t串:(不同的题,我们迭代的方式不同。)
获取当前t.charAt(i),
(1)若map中没有对应key,说明不是异位词
(2)若有key,将对应value-1,若减为0,则将count-1。
(3)若有key,且对应value已经等于0,说明不是异位词。
最后判断count是否为0,若为0说明是异位词。
以aaba为例:
3 - 根据题干优化
如果题干说只有小写或大写字母这种情况,即固定了出现可能的集合,则我们可以使用一个数组代替哈希表
s串每次迭代只需要这样:++count[s.charAt(i) - 'a'];
三 - 例题1:找到字符串所有字母异位词
题目索引:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
给定两个字符串
s
和p
,找到s
中所有p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
1 - 使用哈希表题解:详细注解
class Solution {public List<Integer> findAnagrams(String s, String p) { Map<Character, Integer> map = new HashMap<>(); int count = 0; List<Integer> result = new ArrayList<>(); for(int i=0; i<p.length(); i++){ Integer temp = map.get(p.charAt(i)); map.put(p.charAt(i), temp==null? 1: temp+1); if(temp==null) count++; } int p1=0, p2=0; boolean flag; while(p2<s.length()){ // 迭代p2 Integer temp2 = map.get(s.charAt(p2)); if(temp2==null){ // 包含p2的都不可能,因此结束 flag = false; }else{ flag = true; map.put(s.charAt(p2), temp2-1); if(temp2==1)count--; } p2++; // 迭代p1, 根据p2情况进行迭代 // 1.若p2存在, 则当p2-p1==p.length()时才进入 // 若p2不存在, 则迭代至p1==p2 while(flag? p2-p1==p.length(): p1<p2-1){ // 只有p2-1时有null判断 if(count==0)result.add(p1); // 将p1位置给去除 Integer temp1 = map.get(s.charAt(p1)); if(temp1==0)count++; map.put(s.charAt(p1), temp1+1); p1++; } if(!flag)p1++; // p1=p2-1时候一定为null, 跳过 } // 主迭代结束 return result;}}
2 - 使用数组题解:注解
class Solution {public List<Integer> findAnagrams(String s, String p) { int sLen = s.length(), pLen = p.length(); if (sLen < pLen) { return new ArrayList<Integer>(); } List<Integer> ans = new ArrayList<Integer>(); int[] count = new int[26]; for (int i = 0; i < pLen; ++i) { ++count[s.charAt(i) - 'a']; --count[p.charAt(i) - 'a']; } int differ = 0; for (int j = 0; j < 26; ++j) { if (count[j] != 0) { ++differ; } } if (differ == 0) { ans.add(0); } for (int i = 0; i < sLen - pLen; ++i) { if (count[s.charAt(i) - 'a'] == 1) { // 窗口中字母 s[i] 的数量与字符串 p 中的数量从不同变得相同 --differ; } else if (count[s.charAt(i) - 'a'] == 0) { // 窗口中字母 s[i] 的数量与字符串 p 中的数量从相同变得不同 ++differ; } --count[s.charAt(i) - 'a']; if (count[s.charAt(i + pLen) - 'a'] == -1) { // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从不同变得相同 --differ; } else if (count[s.charAt(i + pLen) - 'a'] == 0) { // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从相同变得不同 ++differ; } ++count[s.charAt(i + pLen) - 'a']; if (differ == 0) { ans.add(i + 1); } } return ans;}}
四 - 例题2:最小覆盖子串
题目索引:https://leetcode.cn/problems/minimum-window-substring/description/
给你一个字符串
s
、一个字符串t
。返回s
中涵盖t
所有字符的最小子串。如果s
中不存在涵盖t
所有字符的子串,则返回空字符串""
。
1 - 思路
例子:s = "ADOBECODEBANC", t = "ABC"
使用双指针:
(1)左指针用来将map和count恢复
(2)右指针用来将map对应的value减一,并改变count
当count=0时,说明此时符合条件,则将此时的子串记录下来。
2 - 题解:详细注解
维护一个数量count和map, 先迭代t将map填充,然后让count = map.size
(1)p2遍历到的如果在map里就将map里对应元素-1, 如果刚好减到0就count-1
(2)如果count==0, 则开始遍历p1,
每次让p1对应到map的元素+1, 每次迭代判断长度, 若小于min则更新
class Solution {public static String minWindow(String s, String t) {Map<Character, Integer> map = new HashMap<>();int min=Integer.MAX_VALUE, index=0, count;Integer temp;for(int i=0; i<t.length(); i++){temp = map.get(t.charAt(i));map.put(t.charAt(i), temp==null? 1: temp+1);}count = map.size();int p1=0, p2=0;for(;p2<s.length();p2++){// 添加p2位置temp = map.get(s.charAt(p2));if(temp==null)continue;// 如果为1说明减完p2元素会归0, 则count变化map.put(s.charAt(p2), temp-1);if(temp==1) count--;// 迭代p1while(count==0){if(p2-p1+1<min) {min = p2-p1+1;index = p1;}// 去掉当前p1的元素temp = map.get(s.charAt(p1));p1++;if(temp==null)continue;map.put(s.charAt(p1-1), temp+1);if(temp==0) count++;}}if(min == Integer.MAX_VALUE)return "";return s.substring(index, index+min);}
}
五 - 结尾
感谢你看到这里,如果感觉内容不错的话请点赞支持一下!
如果小伙伴对我的讲解有疑问,欢迎评论区讨论。