滑动窗口:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
官解的滑动窗口非常易于理解,左指针每次+1,右指针从当前位置开始往后找,使用HashSet来排除重复元素
class Solution {
public int lengthOfLongestSubstring(String s) {
// 哈希集合,记录每个字符是否出现过
Set<Character> occ = new HashSet<Character>();
int n = s.length();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int rk = -1, ans = 0;
for (int i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.remove(s.charAt(i - 1));
}
while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
// 不断地移动右指针
occ.add(s.charAt(rk + 1));
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = Math.max(ans, rk - i + 1);
}
return ans;
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solutions/1959540/xia-biao-zong-suan-cuo-qing-kan-zhe-by-e-iaks/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
但是可以发现,使用一个一个滑的滑动窗口会有很多无效循环,比如abcddfghj,d出现重复,但是在abcd中的最后一位,一位一位的滑,会导致b、c、d开始的滑动其实都无意义。
改进:
*使用HashMap来记录当前遍历情况下的所有出现字母索引的最新情况,如果出现了一个字符在map中存在,判断这个字符的索引是否大于start,如果大于,说明是在当前的最大字符串中,所以从字符索引的下一字符重新开始,如果不在,则不管。
*比较当前字符串的长度和max的值,进行更新。
*将ch的索引值更新,保证它的索引为最新情况
class Solution {
public int lengthOfLongestSubstring(String s) {
//用map记下每个字符的索引,直接跳转
HashMap<Character,Integer> map=new HashMap<>();
int n=s.length();
int start=0;
int ans=0;
for(int end=0;end<n;end++){
char ch=s.charAt(end);
if(map.containsKey(ch)){
start=Math.max(map.get(ch)+1,start);
}
ans=Math.max(ans,end-start+1);
//替换掉ch的索引,每个ch的索引都是当前遍历情况下最新的
map.put(ch,end);
}
return ans;
}
}
技巧:用整数数组来存储字母,易于比较,不用考虑字母顺序
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int[] pnum=new int[26];
//统计p字符串
for(int i=0;i<p.length();i++){
pnum[p.charAt(i)-'a']++;
}
int[] snum=new int[26];
int left=0,right=0;
List<Integer> ans=new ArrayList<>();
while(right<s.length()){
//更新snum
snum[s.charAt(right)-'a']++;
//如果字符长度相等,进行比较
if(right-left+1==p.length()){
if(Arrays.equals(snum,pnum)){
ans.add(left);
}
//将left移除snum
snum[s.charAt(left)-'a']--;
left++;
}
//往后走
right++;
}
return ans;
}
}