【LeetCode】P3 无重复字符的最长子串

P3 无重复字符的最长子串

题目链接:3. 无重复字符的最长子串.

题目描述

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例:

输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3

输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。

输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

题解

方法一:暴力算法

思路

找到从每一个字符为开头的无重复字符的最长子串,那么其中最长的子串就是要寻找的无重复字符的最长子串。

在这里插入图片描述
还需要解决的一个问题是,如何判断字串中仅含有唯一字符,在寻找以一字符为开头的无重复字符的最长子串时,每次对子串的延长,都要判断即将要加入子串的字符是否已经被现有子串所包含,有两种做法:

  • 遍历:遍历子串中有无与之重复的字符
  • hashset:将子串先存入hashset中,再检查有无与之重复的字符

算法

这里采用hashset来判断字串中是否仅含有唯一字符

class Solution {
public:
	int lengthOfLongestSubstring(string s) {
		int ans=0;
		unordered_set<char> hashset;
		for(int begin=0;begin<s.size();++begin){
			hashset.clear();
			int end=begin;	//导致时间复杂度为O(n^2)
			while(!hashset.count(s[end])&&end<s.size()){
				hashset.insert(s[end]);
				++end;
			}
			ans=(end-begin>ans)?(end-begin):ans;
			if(end==s.size()){
				break;
			}
		}
		return ans;
	}
};

复杂度分析

假设 n n n 表示字符串 s s s 的长度

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)

  • 空间复杂度: O ( ∣ Σ ∣ ) O(|\Sigma|) O(Σ),其中 Σ \Sigma Σ 表示字符集(即字符串中可以出现的字符), ∣ Σ ∣ |\Sigma| Σ 表示字符集的大小。在本题中没有明确说明字符集,因此可以默认为所有 ASCII 码在 [ 0 , 128 ) [0, 128) [0,128) 内的字符,即 ∣ Σ ∣ = 128 |\Sigma|=128 Σ=128。我们需要用到哈希集合来存储出现过的字符,而字符最多有 ∣ Σ ∣ |\Sigma| Σ 个,因此空间复杂度为 O ( ∣ Σ ∣ ) O(|\Sigma|) O(Σ)

方法二:滑动窗口(双指针)

思路

由暴力算法,我们可以得到启发,在寻找以每个字符为开头的无重复字符的最长子串时,子串的起始位置呈递增,同时子串的终止位置也是呈递增的,这就像一个大小会变化、但是一直在字符串上向前滑动的窗口。当我们选择第 b e g i n begin begin 个字符作为开头来寻找子串,得到无重复字符的最长子串的结束为第 e n d − 1 end-1 end1 个字符,那么在以第 b e g i n + 1 begin+1 begin+1 个字符作为开头时, [ b e g i n + 1 , e n d ) [begin+1,end) [begin+1,end) 范围内的字符是不重复的,此时,我们可以继续尝试扩大子串的结束位置,检验第 e n d end end 个字符是否与 [ b e g i n + 1 , e n d ) [begin+1,end) [begin+1,end) 范围内的字符重复……一直向后检验,直到出现重复字符时,就得到以第 b e g i n + 1 begin+1 begin+1 个字符作为开头的无重复字符的最长子串,其长度为 e n d − b e g i n end-begin endbegin

在这里插入图片描述
我们先使用双指针来判断有无重复字符

算法

伪代码:

  • ①创建双指针begin=0end=0
  • ②开始滑动窗口,直到end到达字符串结尾(这里的结尾是指超尾位置)
    • 遍历检查[begin,end-1)范围内有无字符与第end个字符重复
      • 若有字符与之重复(记为第i个字符,i∈[begin,end-1) ),那么begin=i+1,结束遍历检查
    • ++end
    • 记录无重复字符子串的最长长度ans
  • ③返回ans
class Solution {
public:
	int lengthOfLongestSubstring(string s) {
		int begin=0;
		int end=0;
		int ans=0;

		while(end<s.size()){
			for(int i=begin;i<end;++i){
				if(s[i]==s[end]){
					begin=i+1;
					break;
				}
			}
			++end;
			ans=(end-begin)>ans?(end-begin):ans;
		}
		return ans;
	}
};

复杂度分析

假设 n n n 表示字符串 s s s 的长度

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1),因为只设置了两指针

方法三:滑动窗口(hashset)

思路

还是使用滑动窗口的思想,只是使用hashset来判断有无重复字符以减少时间复杂度。

算法

伪代码:

  • ①创建双指针begin=0end=0hashset
  • ②开始滑动窗口,直到end到达字符串结尾(这里的结尾是指超尾位置)
    • begin!=0,从hashset中擦除第(begin-1)个字符
    • hashset中无第end个字符 且 end未到达超尾位置
      • true:将第end个字符加入hashset中,++end
      • false:结束循环
    • 记录无重复字符子串的最长长度ans
  • ③返回ans
class Solution {
public:
	int lengthOfLongestSubstring(string s) {
		unordered_set<char> hashset;
		int begin=0;
		int end=0;
		int ans=0;

		for(begin=0;begin<s.size();++begin){
			if(begin!=0){
				hashset.erase(s[begin-1]);
			}
			while(!hashset.count(s[end])&&end<s.size()){
				hashset.insert(s[end]);
				++end;
			}
			ans=(end-begin>ans)?(end-begin):ans;
			if(end==s.size()){
				break;
			}
		}
		return ans;
	}
};

复杂度分析

假设 n n n 表示字符串 s s s 的长度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( ∣ Σ ∣ ) O(|\Sigma|) O(Σ),其中 Σ \Sigma Σ 表示字符集(即字符串中可以出现的字符), ∣ Σ ∣ |\Sigma| Σ 表示字符集的大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值