Open azl397985856 opened 2 years ago
滑动窗口(还不是特别会)
import java.util.HashMap;
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Integer,Integer> map = new HashMap<>();
int left = 0;
int right = 0;
int m = s.length();
int ans = 0;
while (right < m)
{
int t = s.charAt(right) - 'a';
while(map.getOrDefault(t,0) > 0 && left < right)
{
int t2 = s.charAt(left) - 'a';
map.put(t2, map.getOrDefault(t2, 0) - 1);
left += 1;
}
map.put(t, map.getOrDefault(t,0) + 1);
right += 1;
if (right-left > ans)
{
ans = right - left;
}
}
return ans;
}
}
复杂度分析
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
# 哈希集合,记录每个字符是否出现过
occ = set()
n = len(s)
# 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
rk, ans = -1, 0
for i in range(n):
if i != 0:
# 左指针向右移动一格,移除一个字符
occ.remove(s[i - 1])
while rk + 1 < n and s[rk + 1] not in occ:
# 不断地移动右指针
occ.add(s[rk + 1])
rk += 1
# 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk - i + 1)
return ans
学习官方题解
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// 哈希集合,记录每个字符是否出现过
unordered_set<char> occ;
int n = s.size();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int rk = -1, ans = 0;
// 枚举左指针的位置,初始值隐性地表示为 -1
for (int i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.erase(s[i - 1]);
}
while (rk + 1 < n && !occ.count(s[rk + 1])) {
// 不断地移动右指针
occ.insert(s[rk + 1]);
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk - i + 1);
}
return ans;
}
};
JavaScript Code:
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
const occ = new Set();
const n = s.length;
let rk = -1, ans = 0;
for (let i = 0; i < n; ++i) {
if (i != 0) {
occ.delete(s.charAt(i - 1));
}
while (rk + 1 < n && !occ.has(s.charAt(rk + 1))) {
occ.add(s.charAt(rk + 1));
++rk;
}
ans = Math.max(ans, rk - i + 1);
}
return ans;
};
复杂度分析
令 n 为数组长度。
public int lengthOfLongestSubstring(String s) {
if (s == null){
return 0;
}
HashMap<Character,Integer> map = new HashMap<>();
int length = s.length();
int res = 0;
for (int start = 0, end = 0; end < length; end++) {
char c = s.charAt(end);
if (map.containsKey(c)){
start = Math.max(map.get(c)+1,start);
}
res = Math.max(res,end - start + 1);
map.put(c,end);
}
return res;
}
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> occ = new HashSet<Character>();
int n = s.length();
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;
}
ans = Math.max(ans, rk - i + 1);
}
return ans;
}
}
Idea: Two Pointers with HashSet
Code: public class Solution { public int LengthOfLongestSubstring(string s) {
HashSet<char> strSet = new HashSet<char>();
int left = 0;
int right = 0;
int maxLen = 0;
while(right < s.Length)
{
while(strSet.Contains(s[right]))
{
strSet.Remove(s[left++]);
}
strSet.Add(s[right]);
maxLen = Math.Max(maxLen, right - left + 1);
right++;
}
return maxLen;
}
}
每天靠标签知道用什么方法,自己还是判断不出来,今天是学习滑动窗口的一天
public int lengthOfLongestSubstring(String s) {
if (s.length()==0) return 0;
HashMap<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;
}
复杂度分析 -时间复杂度:O(N) -空间复杂度:O(N)
之前已经有做过,再重新做一遍,结合双指针和hash表,从头开始循环,维护一个不重复的子字符串区间,left表示区间起点,用map存储遍历过的字符,当当前字符存在于map并且对应索引大于等于left时表示该字符已在区间中,更新left为重复字符位置+1,否则计算max = Math.max(max, i - left + 1)
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
if (s.length < 2) {
return s.length;
}
let left = 0
let max = 1
const map = new Map()
for (let i = 0; i < s.length; i++) {
if (map.has(s[i]) && map.get(s[i]) >= left) {
left = map.get(s[i]) + 1
} else {
max = Math.max(max, i - left + 1)
}
map.set(s[i], i)
}
return max
}
空间O(N),时间O(N),N为字符串长度
思路 滑动窗口,遇到不同字符窗口扩大,使用map存储字符以及结果,最后不断更新返回结果 代码
class Solution {
public int lengthOfLongestSubstring(String s) {
int length = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>();
for (int end = 0, start = 0; end < length; end++) {
char c = s.charAt(end);
if (map.containsKey(c)) {
start = Math.max(map.get(c), start);
}
ans = Math.max(ans, end - start + 1);
map.put(s.charAt(end), end + 1);
}
return ans;
}
}
复杂度 时间复杂度O(N) 空间复杂度O(N)
还就那个经典
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<int> m(128, 0);
int ans = 0;
int i = 0;
for (int j = 0; j < s.size(); j++) {
i = max(i, m[s[j]]);
m[s[j]] = j + 1;
ans = max(ans, j - i + 1);
}
return ans;
}
};
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> occ = new HashSet<Character>();
int n = s.length();
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;
}
ans = Math.max(ans, rk - i + 1);
}
return ans;
}
}
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
n = len(s)
dic = {} # 哈希表记录每个字符(上一次)出现的位置
dic[s[0]] = 0
dp = [1] * n # dp[i],以s[i]结尾的最长无重复字符串
for j, ch in enumerate(s[1:], start=1):
i = dic.get(ch, -1) # ch上次出现的位置
dic[ch] = j # 更新ch出现的位置
dp[j] = min(dp[j-1] + 1, j-i) # 状态转移方程
return max(dp)
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
ans = 0
dic = {} # 哈希表记录每个字符(上一次)出现的位置
left = -1 # 无重复字符串的左边界
for right, ch in enumerate(s):
# i = dic.get(ch, -1) # ch上次出现的位置;默认为-1,即ch未出现过
# left = max(i, left) # 更新左边界
if ch in dic: # ch出现过
left = max(dic[ch], left) # 更新左边界
dic[ch] = right # 更新ch出现的位置
ans = max(ans, right-left)
return ans
复杂度分析
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n = s.size();
int ret = 0;
int right = -1;
unordered_set<char> mp;
for(int i=0; i<n; i++){
if (i != 0){
mp.erase(s[i-1]);
}
while(right + 1 < n && !mp.count(s[right+1])){
mp.insert(s[right+1]);
right++;
}
ret = max(ret, right - i + 1);
}
return ret;
}
};
思路:滑动窗口+哈希表 时间复杂度O(nk) class Solution: def lengthOfLongestSubstring(self, s: str) -> int: if s=="": return 0 ans =1 for i in range(len(s)-1): dic = {s[i]:1} leng = 1 for j in range(i+1,len(s)): if s[j] in dic: break else: leng +=1 dic[s[j]]=1 ans = max(ans,leng) return ans
滑动窗口
class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>();
for (int end = 0, start = 0; end < n; end++) {
char alpha = s.charAt(end);
if (map.containsKey(alpha)) {
start = Math.max(map.get(alpha), start);
}
ans = Math.max(ans, end - start + 1);
map.put(s.charAt(end), end + 1);
}
return ans;
}
}
动态规划,dp[i]记录到该字符到最大不重复字符数,用哈希表记录字符大小。
func maxNum(a,b int)int{
if a>b{
return a
}
return b
}
func minNum(a,b int)int{
if a>b{
return b
}
return a
}
func lengthOfLongestSubstring(s string) int {
m := map[byte]int{}
dp := make([]int,len(s)+1)
max := 0
for i:=1;i<=len(s);i++{
if v,ok := m[s[i-1]];ok{
dp[i] = minNum(i-v,dp[i-1]+1)
}else{
dp[i] = dp[i-1]+1
}
m[s[i-1]]=i
max = maxNum(max,dp[i])
}
return max
}
时间复杂度O(n)
空间复杂度O(n+m),n为dp的存储空间,m为哈希表中字符集大小
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
class Solution {
public int lengthOfLongestSubstring(String s) {
//双指针 + 哈希表
//左闭右开
int left = 0;
int right = 0;
int length = s.length();
//长度最大值
int max = 0;
Map<Integer,Integer> map = new HashMap<>();
while(right < length && left <= right){
char newCharacter = s.charAt(right);
//重复了
if(map.containsKey(newCharacter - 'a')){
//更新max
max = Math.max(max, right - left);
int repeatIndex = map.get(newCharacter - 'a');
//把map中当前left到repeatIndex的元素全部删掉
//原来说:不要删repeatIndex,因为新加入的也是这个字母,这是错误的!因为你的value也就是坐标值是变化了的,必须要删掉重新更新
for(int i = left; i <= repeatIndex; i++){
map.remove(s.charAt(i) - 'a');
}
//left指向原来的重复元素的后一个元素的下标
left = repeatIndex + 1;
}
//把新节点加进map中,value值为该节点的坐标
map.put(newCharacter - 'a', right);
right ++;
}
return Math.max(max, right - left);
}
}
时间复杂度:$O(n)$
空间复杂度:$O(n)$
hash记录索引是否出现过,然后while遍历
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// 哈希集合,记录每个字符是否出现过
unordered_set<char> occ;
int n = s.size();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int rk = -1, ans = 0;
// 枚举左指针的位置,初始值隐性地表示为 -1
for (int i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.erase(s[i - 1]);
}
while (rk + 1 < n && !occ.count(s[rk + 1])) {
// 不断地移动右指针
occ.insert(s[rk + 1]);
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk - i + 1);
}
return ans;
}
};
C++ Code:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if(s.size()==0)
return 0;
unordered_map<char,int> mp;
int l = 0, r = 1;
int ret = 1;
mp[s[0]] = 0;
while(r<s.size())
{
if(mp.count(s[r])==0||mp[s[r]]==-1)
{
mp[s[r]] = r;
ret = max(ret, r+1-l);
}
else
{
while(s[l]!=s[r])
mp[s[l++]] = -1;
l++;
mp[s[r]] = r;
}
++r;
}
return ret;
}
};
思路 遍历右边 直到找到第一个重复。停止 记录长度 左边上到前方重复字符右边一个的位置 用来避免重复 直到右边抵达边框,看看最后长度,然后返回最长的长度
代码
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
left = 0
res = 0
for i, ch in enumerate(s):
if ch in s[left:i]:
res = max(res, i-1-left+1)
left = left + s[left:i].index(ch) + 1
res = max(res, len(s)-1-left+1)
return res
复杂度 时间 O(n) 空间 O(1)
class Solution {
public int lengthOfLongestSubstring(String s) {
Set
class Solution {
public int lengthOfLongestSubstring(String s) {
// 无重复、最长子串、的长度
Set<Character> charCount = new HashSet<>();
char[] strArray = s.toCharArray();
int maxCount = 0;
int l = 0;
for (int i = 0; i < strArray.length; i++) {
if (charCount.contains(strArray[i])) {
// 只要出现重复 删除元素直至 set不包含即将添加的元素
while (l < i && charCount.contains(strArray[i])) {
charCount.remove(strArray[l]);
l++;
}
}
// 加入新元素 更新max
charCount.add(strArray[i]);
maxCount = Math.max(charCount.size(), maxCount);
}
return maxCount;
}
}
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if s=='':
return 0
ml=1
for i in range(len(s)):
e=[]
e.append(s[i])
ans=1
for j in range(i+1,len(s)):
if s[j] in e:
break
e.append(s[j])
ans+=1
if ans>ml:
ml=ans
return ml
class Solution {
public int lengthOfLongestSubstring(String s) {
if (s.length()==0) return 0;
HashMap<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;
}
}
map过滤重复元素,并且更新最长子串
const set = new Set()
let i = 0, j = 0, maxLength = 0
if(s.length === 0) return 0
for(i; i < s.length; i++) {
if(!set.has(s[i])) {
set.add(s[i])
maxLength = Math.max(maxLength, set.size)
} else {
while(set.has(s[i])) {
set.delete(s[j])
j++
}
set.add(s[i])
}
}
return maxLength
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s == null || s.length() == 0) return 0;
int res = 0;
Map<Character, Integer> map = new HashMap<>();
for(int i = 0, j = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(map.containsKey(c)) {
j = Math.max(j, map.get(c) + 1);
}
map.put(c, i);
res = Math.max(res, i - j + 1);
}
return res;
}
}
复杂度分析
滑动窗口
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if (len(s) == 0) or (len(s) == 1):
return len(s)
# 滑动窗口的左右边界
l,r = 0,0
# 结果
ret = 0
# 保存最后一次出现的位置
char_dict = {}
char_dict[s[0]] = 0
for r in range(1,len(s)):
if (s[r] in char_dict) and char_dict[s[r]] >= l:
l = char_dict[s[r]] + 1
char_dict[s[r]] = r
ret = max(ret,r-l+1)
return ret
滑动窗口 维护2个索引, left和right, 用来控制窗口的边界; 创建一个set用于存储窗口里的值, 先移动right索引, 然后取字符串对应索引的值.
var lengthOfLongestSubstring = function(s) {
let ans = 0
let left = 0
let right = 0
const charSet = new Set()
while(right < s.length) {
const rightChar = s[right]
while(charSet.has(rightChar)) {
const leftChar = s[left]
left++
charSet.delete(leftChar)
}
charSet.add(rightChar)
right++
ans = Math.max(ans, charSet.size)
}
return ans
};
暴力解,如果遇到重复值,则把索引值向前回归,回到重复值的下一位。
JavaScript Code:
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
let str = ''
let max = 0
for(let i = 0; i < s.length; i++) {
if (str.indexOf(s[i]) === -1) {
str = str + s[i]
max = Math.max(str.length, max)
} else {
str = ''
let index = i - 1
while(s[i] !== s[index]) {
index--
}
i = index
}
}
return max
};
复杂度分析
令 n 为数组长度。
滑动窗口 + hashmap
左右指针,用hashmap存储每个字符串出现过的位置,如果右指针指向的值,在hashmap中的坐标,是在左右指针中间,则需要更改窗口大小。将左指针指向该坐标+1.更新hashmap中该字符的坐标。
JavaScript Code:
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
let left = (right = 0)
let max = 0
let map = {}
while(right < s.length) {
const pos = map[s[right]];
if (pos >= left && pos <= right) {
left = pos + 1
}
map[s[right]] = right
max = Math.max(max, right - left + 1)
right++
}
return max
};
复杂度分析
令 n 为数组长度。
重复以上步骤直至遍历完s,此时的res就是最长字串长度
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
if (!s) return 0;
const len = s.length;
if (len === 1) return 1;
let res = 0;
let curStr = '';
for(let i = 0; i < len; i++) {
const cur = s[i];
if (!curStr.includes(cur)) {
curStr += cur;
} else {
res = curStr.length > res? curStr.length: res;
const dupIndex = curStr.indexOf(cur);
curStr = curStr.slice(dupIndex+1, curStr.length) + cur;
}
}
res = curStr.length > res? curStr.length: res;
return res
};
时间复杂度:O(N) N为s长度 空间复杂度:O(N)
滑动窗口
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
max_len = 1
start = 0
end = 0
for i in range(len(s)):
end = i
if start == end:
continue
# 使用Set优化时间复杂度
if s[i] in s[start:end]:
max_len = max(max_len, end - start)
while s[i] in s[start:end] and start <= end:
start += 1
else:
max_len = max(max_len, end - start + 1)
return max_len
var lengthOfLongestSubstring = function(s) { const hash = new Set() let res = 0, right = -1 //右指针从左指针左边开始 for(let left = 0; left < s.length; left++){ if(left != 0){ hash.delete(s.charAt(left-1)) } while(right+1 < s.length && !hash.has(s.charAt(right+1))){ hash.add(s.charAt(right+1)) right++ } res = Math.max(res, right - left + 1) } return res };
滑动窗口 维护2个索引, left和right, 用来控制窗口的边界; 创建一个set用于存储窗口里的值, 先移动right索引, 然后取字符串对应索引的值.
var lengthOfLongestSubstring = function(s) {
let ans = 0
let left = 0
let right = 0
const charSet = new Set()
while(right < s.length) {
const rightChar = s[right]
while(charSet.has(rightChar)) {
const leftChar = s[left]
left++
charSet.delete(leftChar)
}
charSet.add(rightChar)
right++
ans = Math.max(ans, charSet.size)
}
return ans
};
use Set to store the current information
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
left, right = 0, 0
maxLen = 0
mySet = set()
while right < len(s):
if s[right] not in mySet:
mySet.add(s[right])
else:
while s[right] in mySet:
mySet.remove(s[left])
left+=1
mySet.add(s[right])
if right-left+1 > maxLen:
maxLen = right-left+1
right+=1
return maxLen
Time: O(n) Space:O(n)
3. 无重复字符的最长子串
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
前置知识
题目描述
示例 1:
输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 示例 3:
输入: "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。