您的位置:首页 > 其它

LeetCode 3 Longest Substring Without Repeating Characters

2018-02-07 11:31 627 查看

Solution

1 Brute Force

注意:

子串(substring)指的是字符串中几个连续 的字符组成的字符串。

子序列(subsequence)指的是字符中几个顺序但不一定连续 的字符组成的字符串。

Intuition:先考虑如何检查一个字符串中是否有重复字符。检查字符串的每个子串是否有重复字符,如果没有则记录其长度。

Algorithm: 假设有个函数
boolean allUnique(String  substring)
用来判断一个字符串是否有重复字符。

那么如何检查每个子串?只要确定了起始的indice和终止的indice就确定了一个子串。

那么如何判断一个字符串是否有重复字符?可以用set,set中不能包含两个相同元素。

Runtime BF算法会超时

时间复杂度:O(n3)O(n3),见题目solution分析

空间复杂度:需要对子串进行存储,需要的空间是O(k)O(k),k就是
Set
的尺寸,
Set
的尺寸取决于字符串长度n和字符集(chasrset)大小m(字符串中的字符取自多大的字符集,即最多有多少个无重复字符)。所以空间复杂度为O(min(n,m))O(min(n,m))。

2 Sliding Window

Algorithm

暴力很直接(我竟然也没思路。。。),但是太慢,所以如何去优化?

用暴力方法时,我们重复的检查一个子串来看是否有重复子串。但是这是没必要的。如果子串SijSij中的第i个字符到第j-1个字符的组成的子串已经确认过没有重复字符,我们只需要确认字符S[j]S[j]是否已经在子串中。

检查一个字符是否已经在子串中,需要扫描子串,时间复杂度为O(n2)O(n2)。如果使用HashSet 作为滑动窗口,复杂度为O(1)O(1)。

A sliding window 是一个经常在 数组/字符串(array/string) 问题中经常使用的抽象概念。一个窗口是由起始索引和结束索引定义的一系列元素(左闭右开)。一个滑动窗口是指窗口的两个边界向特定方向“滑动”。比如将窗口边界 [i,j)[i,j) 向右移动一个元素,边界就变成了[i+1,j+1)[i<
adc0
mo>+1,j+1) (左闭右开)。

回到本题目,使用HashSet来储存当前窗口[i,j)[i,j)的字符。然后将j向右移动,如果新字符没有在HashSet中,则继续右移,直到S[j]S[j] 已经在字符中了。此时,我们就找出了以i为为起始索引的最大无重复子串。对i进行遍历,即可得到结果。

总结:思路是先考虑到暴力时,判断一个子串有无重复元素时,是直接对该子串进行判断,但该子串的子串可能已经是判断过的,所以就会做重复的工作。那么如何避免做重复的工作? 在滑动窗口的概念中,先固定起始索引,令结束索引右移,如果窗口中有了重复字符,则没有必要再右移结束索引。

滑动窗口的思路适用于 数组/字符串 中的连续元素问题。

时间复杂度:

3 Sliding Window Optimized

对第二种方法进行优化,考虑到在第二步中对窗口进行检查时,固定起始索引,右移结束索引直到字符已经在窗口中重复出现,之后要右移起始索引,注意第二种方法中是将起始索引右移了1个元素,但是其实可以右移多个元素,直到出现重复字符。

看solution的说明比较详细。

虽然思路是这样,但是看给的代码还是没懂。代码中实现的方式是,用i,j 表示窗口的起始和结束,这个窗口是抽象的,并没有将元素加入map中。同时,j 不断加1,求 j 对应的无重复子串的起始索引 i 。如何求? 当j 右移时,判断元素s[j] 之前是否出现过(在迭代时,将每个元素(key)和对应索引(value)都加入了map中),使用
count()
find()
可以查到。当出现重复元素时,判断是否更新i 为该元素在之前最后出现时的索引(即该元素在map中对应的value),如果i要更新的值大于i (即起点如果不需要后移,则不更新起点),并且更新map中该元素对应的索引,使其保持为最大。此时i 则表示无重复子串的起点。

相关知识点

c++ set

count(const key_type &key );
返回值为key 的元素的个数(0或1),set中没有重复元素,返回值为0或1。

感觉这么总结也太慢了,之后只总结知识点,思路还是直接去原地址看吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: