3. 无重复字符的最长子串
1. 题目
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
2. 代码
c++
#include <stdio.h>
#include <unordered_set>
#include <math.h>
#include <iostream>
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int length = s.length();
if (length == 0) { return 0; }
int left = 0;
int maxStr = 0;
// 方法一
// std::unordered_set<char> set;
// for (int i = 0; i <length; i++) {
// while (set.find(s[i]) != set.end()){ //找得到
// set.erase(s[left]);
// left++;
// }
// set.insert(s[i]);
// maxStr = std::max(maxStr, i-left+1);
// }
// 方法二
std::unordered_map<char,int> map;
for (int i = 0; i <length; i++) {
if (map.find(s[i]) != map.end()) {
left = std::max(left, map[s[i]] + 1);
}
map[s[i]] = i;
maxStr = std::max(maxStr, i-left+1);
}
return maxStr;
}
};
java
public int lengthOfLongestSubstring(String s) {
if (s.length()==0) return 0;
Map<Character, Integer> map = new HashMap<Character, Integer>();
int max = 0;
int left = 0;
for(int i = 0; i < s.length(); i ++){
if(map.containsKey(s.charAt(i))){
left = Math.max(left,map.get(s.charAt(i)) + 1);
}
map.put(s.charAt(i),i);
max = Math.max(max,i-left+1);
}
return max;
}
3. 题解
滑动窗口算法在一个特定大小的字符串或数组上进行操作,而不在整个字符串和数组上操作,这样就降低了问题的复杂度,从而也达到降低了循环的嵌套深度
遍历string中字符,找出不重复的子字符串存入集合,不重复子字符串的满足条件是最后一个字符+1 与之前集合中字符重复,此时从集合中删除重复字符以及该字符前面的字符,窗口滑动到重复字符后面一个字符
567. 字符串的排列
1. 题目
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false
输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").
2. 代码
自己想到的解法,提交超时了
bool checkInclusion(std::string s1, std::string s2) {
int n1 = s1.length();
int n2 = s2.length();
if (n1 > n2) {
return false;
}
// 将s1中每个字符串中的字符以及字符个数存入map
std::map<char, int> map1;
for (int i = 0; i < n1; i++) {
map1[s1[i]]++;
}
std::map<char, int> map2;
int left = 0;
while ((left + n1 - 1) < n2) {
int flag = 1;
map2.clear();
// 将s2中长度为n1的子字符串中每个字符串中的字符以及字符个数存入map
for (int i = left; i < left+n1; i++) {
map2[s2[i]]++;
}
// 比较两个map是否相等
for (auto it: map2) {
auto iter = map1.find(it.first);
if (iter == map1.end()) {
flag = 0;
break;
}
if (it.second != iter->second){
flag = 0;
break;
}
}
// 相等返回true
if (flag) { return true;}
// 不相等,继续比较下一个窗口
left ++;
}
return false;
}
官方示例:
class Solution {
public:
bool checkInclusion(string s1, string s2) {
int n = s1.length(), m = s2.length();
if (n > m) {
return false;
}
vector<int> cnt1(26), cnt2(26);
for (int i = 0; i < n; ++i) {
// 将字符串当前位置的字符转化为cnt的下标,一一对应起来,0<=s[i]-'a'<=26,cnt初始化的时候就是长度就是26
++cnt1[s1[i] - 'a'];
++cnt2[s2[i] - 'a'];
}
// for (int a : cnt1) {std::cout << a ;}
// std::cout <<std::endl;
// for (int b : cnt2) {std::cout << b ;}
// std::cout <<std::endl;
if (cnt1 == cnt2) {
return true;
}
for (int i = n; i < m; ++i) {
++cnt2[s2[i] - 'a'];
--cnt2[s2[i - n] - 'a'];
// for (int b : cnt2) {std::cout << b ;}
// std::cout <<std::endl;
if (cnt1 == cnt2) {
return true;
}
}
return false;
}
};
java代码
public boolean checkInclusion(String s1, String s2) {
int n = s1.length(), m = s2.length();
if (n > m) {
return false;
}
int[] cnt1 = new int[26];
int[] cnt2 = new int[26];
for (int i = 0; i < n; ++i) {
++cnt1[s1.charAt(i) - 'a'];
++cnt2[s2.charAt(i) - 'a'];
}
if (Arrays.equals(cnt1, cnt2)) {
return true;
}
for (int i = n; i < m; ++i) {
++cnt2[s2.charAt(i) - 'a'];
--cnt2[s2.charAt(i - n) - 'a'];
if (Arrays.equals(cnt1, cnt2)) {
return true;
}
}
return false;
}
3. 题解
- 当两个字符串每个字符的个数均相等时,一个字符串才是另一个字符串的排列
- 使用两个数组分别表示s1 和s2中子串的各个字符的个数
- 使用一个固定长度为 nn 的滑动窗口来维护cnt2,滑动窗口每向右滑动一次,就多统计一次进入窗口的字符,少统计一次离开窗口的字符。然后,判断 cnt1 和 cnt2是否相等,相等就表示找到了是s1排列的子串
- vector size设置为26,代表26个字母长度, 【s1[i] - ‘a’ 】表示当前字符下标值,++cnt1[s1[i] - ‘a’] 是给当前下标赋值,理解不了可以看参照下图, 是上述代码中注释掉的输出log
测试代码
Solution so;
bool ret = so.checkInclusion("ab", "eidoooba");
std::cout << ret << std::endl;