1. leetcode 3-无重复字符的最长子串
1.1 题目描述
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
示例 4:
输入: s = “”
输出: 0
示例 5: 提交错误看到的
输入: s = “dvdf”
输出: 3
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
1.2 代码实现
思路:设置左右指针i和j模拟滑动窗口,当j指向的元素和窗口里没有重复时,j指针右移,同时将元素加入set;当发生重复时,i指针右移,同时从set中删除指向的元素,直至i指针指向重复元素的下一个元素。
我的提交:
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s.length() < 2){
return s.length();
}
int i = 0, j = 1;
int maxLen = 1;
Set<Character> set = new HashSet<>();
set.add(s.charAt(i));
while(i <= j && j < s.length()){
char temp = s.charAt(j);
if(set.contains(temp)){
maxLen = Math.max(maxLen, j - i);
//删除重复字符之前的字符
while(s.charAt(i) != temp){
set.remove(s.charAt(i));
i++;
}
//删除重复字符
set.remove(s.charAt(i));
i++;
}else{
set.add(temp);
j++;
}
}
//与最后一个子串比较
return Math.max(maxLen, set.size());
}
}
时间:O(n);空间:O(m);
收获:看到重复想到用 散列表 来判断,看到 子串 涉及放缩,想到可能用 滑动窗口。
做了第3遍,请牢记这道题!!!
2. leetcode 567-字符串的排列
2.1 题目描述
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的 子串 。
示例 1:
输入: s1 = “ab” s2 = “eidbaooo”
输出: True
解释: s2 包含 s1 的排列之一 (“ba”).
示例 2:
输入: s1= “ab” s2 = “eidboaoo”
输出: False
提示:
1 <= s1.length, s2.length <= 104
s1 和 s2 仅包含小写字母
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-in-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2.2 代码实现
思路:使用一个数组 cnt 记录 s1 和 s2 字符的出现次数之差(相同的字符,对应次数会抵消),使用变量 diff 记录cnt中不为0的数字个数,在滑动窗口的过程中,每次向右滑一格,都会更新diff并判断。
参考官答:
class Solution {
public boolean checkInclusion(String s1, String s2) {
int len1 = s1.length();
int len2 = s2.length();
if(len1 > len2){
return false;
}
//开辟数组记录字符出现次数
int[] cnt = new int[26];
//判断初始窗口中s1, s2的前len1个字符是否相等
for(int i = 0; i < len1; i++){
//注意这里是 s1字符--,s2字符++,和后面滑动窗口时cnt[x]++,cnt[y]--是一致的
//即s2进入窗口的字符是++,离开窗口的字符是--
cnt[s1.charAt(i) - 'a']--;
cnt[s2.charAt(i) - 'a']++;
}
//统计出现不同字符的个数
int diff = 0;
for(int c: cnt){
if(c != 0){
diff++;
}
}
//判断初始窗口是否符合条件
if(diff == 0){
return true;
}
//开始滑动窗口
for(int i = len1; i < len2; i++){
int x = s2.charAt(i) - 'a'; //进窗口的字符
int y = s2.charAt(i - len1) - 'a'; //出窗口的字符
if(x == y){
continue;
}else{
//处理x前判断一下
if(cnt[x] == 0){
diff++;
}
//x进入窗口(这里++是和前面s2入窗口++是一致的)
cnt[x]++;
//处理x后判断一下
if(cnt[x] == 0){
diff--;
}
// y 同理
if(cnt[y] == 0){
diff++;
}
//y离开窗口
cnt[y]--;
if(cnt[y] == 0){
diff--;
}
}
//判断当前窗口是否符合条件
if(diff == 0){
return true;
}
}
return false;
}
}
时间:O(n+m+t),t为开辟数组大小,这里其实常数26; 空间: O(t);