LeetCode之3. Longest Substring Without Repeating Characters
2017-08-31 21:11
411 查看
LeetCode上给的解决办法
尝试 #1 暴力方法
思考一个个查看所有的子字符串看看是否没有相同的字符
算法
——假设我们有一个函数,boolean allUnique(String substring),这个函数在String的每个子字符串中的字符都是两两不同的时候返回True。我们可以迭代字符串S中的所有子字符串并调用allUnique这个函数。如果返回True的话,我们就更新不重复子字符串的长度。
注意
为了列举给定字符串的所有的子字符串,我们列举这些子字符串的开始和结尾。假设开始和结尾分别是i和j。然后我们有0<=i<=j<=n。因此我们用两层嵌套循环分别是i从0到n-1和j从i+1到n,来列举s所有的子字符串。
检查一个字符串是否有相同的字符,我们可以用一个集合。对于字符串中的每一个字符进行迭代,如果没有相同的字符,那么我们把他们一个个放到集合中。在放入集合之前,我们检查这个集合是否已经包含该字符,如果包含了,返回false,在循环结束后,我们返回true。
java代码,由于复杂度较大,所以只在次贴上Leetcode上的例子,自己不再写其他语言
[Time Limit Exceeded]
public class Solution { public int lengthOfLongestSubstring(String s) { int n = s.length(); int ans = 0; for (int i = 0; i < n; i++) for (int j = i + 1; j <= n; j++) if (allUnique(s, i, j)) ans = Math.max(ans, j - i); return ans; } public boolean allUnique(String s, int start, int end) { Set<Character> set = new HashSet<>(); for (int i = start; i < end; i++) { Character ch = s.charAt(i); if (set.contains(ch)) return false; set.add(ch); } return true; } }
复杂度分析
时间复杂度:O(n^3)
—–为了检查范围[i,j)中的所有字符是否是不重复的,我们需要扫描一遍,因此花费时间O(j-i)。对于给定的i,j花费的总时间是
,因此总的时间是
空间复杂度
—–一个算法在运行过程中临时占用存储空间大小的量度,这是百度百科对于空间复杂度的定义,所以我们看复杂度主要是看运行过程中临时占用,比如字符s,就不属于运行过程中临时占用的,因为运行完,还是要存着s。所以应该是子字符串的长度,这个程序运行完后,就不存在了。所以应该是O(min(n,m))。
这个n是字符串s的长度,m是字符串s的字符集的长度,
如果n> m,这个时候字符集的长度小于字符串的长度,也就是字符串中有重复的字符,所以我们取m。
如果n< m,这个时候字符集的长度大于字符串的长度,也就是字符串中没有重复的字符,所以我们取n。
第一种方法,在此,就不做具体实现了,因为本身这种方法很复杂,不想要自己记住这个。
尝试 #2 滑动窗口hashmap
算法原始的方法是很直接的,但是非常慢,那么如何优化呢?
在原始的方法中,我们重复的检查一个子字符串看是否有重复的字符。但这个是不必要的,如果一个子字符串sij,从i到j-1是已经检查没有重复字符的,我们只需要检查s[j]是否已经在sij中就行了。
检查一个字符(给定)是否已经在子字符串中,我们可以通过双层嵌套循环扫描所有子字符串,但这样导致一个O(n^2)的时间复杂度,但是在这里我们可以改进下。
通过使用一个HashSet(java)作为一个滑动窗口,检查一个字符是否已经在子字符串中花费的时间是O(1)。
一个滑动窗口是一个虚拟的概念,这个概念被广泛的用于数组或字符串的问题。一个窗口是数组或字符串的一定范围的元素,通常被开始和结束索引来定义,注意这个窗口滑动的是偶是把两个边界滑动到某一个方向。例如,如果我们呢把[i,j)向右滑动一个,那么就变成了[i+1,j+1)。
回到问题上,我们用HashSet存储当前窗口的字符[i,j)(开始i=j),然后我们把索引j向右滑动。如果它不在,我们仍然向右滑,直到s[j]已经在HashSet中。这个时候这个时候我们就发现了最长不重复子字符串的长度。如果我们对于每一个 i,我们都滑动j,那么就可以得到结果。
java代码,由于复杂度较大,所以只在次贴上Leetcode上的例子,自己不再写其他语言
[Accepted]
这里写代码片public class Solution { public int lengthOfLongestSubstring(String s) { int n = s.length(); Set<Character> set = new HashSet<>(); int ans = 0, i = 0, j = 0; while (i < n && j < n) { // try to extend the range [i, j] if (!set.contains(s.charAt(j))){ set.add(s.charAt(j++)); ans = Math.max(ans, j - i); } else { set.remove(s.charAt(i++)); } } return ans; } }
Python自己写的
""" slide window algorithm range[i,j) if s[j] not in list1, list1.append; if s[j] in list1, i jump to i+1 """ class Solution(object): def lengthOfLongestSubstring(self, s): i, j, ans, list1 = 0, 4000 0, 0, [] while i < len(s) and j < len(s): if s[j] not in list1: list1.append(s[j]) j += 1 ans = max(ans, j - i) else: list1.remove(s[i]) i += 1 # increase i little by little return ans b = Solution() print b.lengthOfLongestSubstring('abcabcdefa')
复杂度分析
时间复杂度:O(2n)=O(n)。最坏情况下,每个字符将会被i和j访问两次。
空间复杂度:O(min(m,n))。和上面的方法一样。
尝试 #3 优化后的滑动窗口hashmap
上面的解决方法需要至少2n步。事实上,可以优化为只需要n步。我们可以定义一个字符与其索引的映射,而不是用一个集合来告诉我们字符是否存在。然后我们可以立即跳过我们发现的重复的字符。原因是如果s[j]在索引j’的范围[i,j)中有重复,我们没必要再一点一点的增加i。我们可以跳过[i,j’]或者[i,j),使i直接赋值为j’+1或j。这样讲可能比较迷惑人,我自己也是在这卡了许久。简单地说就是,如果s[j]重复,下一个i直接从j开始。
很重要,也是让我很难懂的,不懂为什么map.put (,j+1),我知道会对i产生影响,但为什么是j+1呢
java
public class Solution { public int lengthOfLongestSubstring(String s) { int n = s.length(), ans = 0; Map<Character, Integer> map = new HashMap<>(); // current index of character // try to extend the range [i, j] for (int j = 0, i = 0; j < n; j++) { if (map.containsKey(s.charAt(j))) { i = Math.max(map.get(s.charAt(j)), i); } ans = Math.max(ans, j - i + 1); map.put(s.charAt(j), j + 1); } return ans; } }
C++
引用
class Solution { public: int lengthOfLongestSubstring(string s) { unordered_map<char,int> mymap; unordered_map<char,int>::iterator it; int len = 0,i = -1; for(int j=0;j < s.length();j++) { /***是否有重复******/ it = mymap.find(s.at(j)); if(it != mymap.end()) /*****有重复的时候,移动i*****/ i = std::max(it->second,i); /****把新的字符加入*******/ mymap[s.at(j)] = j; len = std::max(len,(j-i)); } return len; } };
Python自己写的
""" slide window algorithm range[i,j) if s[j] not in list1, list1.append; if s[j] in list1, i jump to j """ class Solution(object): def lengthOfLongestSubstring(self, s): i, j, ans, list1 = 0, 0, 0, [] for j in range(0, len(s)): if s[j] not in list1: list1.append(s[j]) ans = max(ans, j - i) else: i = max(s.index(s[j]), i) # increase i to j self.ans = ans return self.ans b = Solution() print b.lengthOfLongestSubstring('abcabced')
swift
后续补上
C
后续补上
复杂度分析
时间复杂度:O(n)。j将会迭代n次。
空间复杂度:O(m)。m是字符集的长度。
尝试 #3 数组
之前的实现都没有字符串集合s的假设,如果我们知道集合是非常的小,我们可以用整形数组代替Map作为可以直接访问的表。常用的表格有:
int[26] for Letters ‘a’ - ‘z’ or ‘A’ - ‘Z’
int[128] for ASCII
int[256] for Extended ASCII
java
public class Solution { public int lengthOfLongestSubstring(String s) { int n = s.length(), ans = 0; int[] index = new int[128]; // current index of character // try to extend the range [i, j] for (int j = 0, i = 0; j < n; j++) { i = Math.max(index[s.charAt(j)], i); ans = Math.max(ans, j - i + 1); index[s.charAt(j)] = j + 1; } return ans; } }
C++
引用
class Solution { public: int lengthOfLongestSubstring(string s) { vector<int> mymap(255,-1); int len = 0,i = -1,tmp; for(int j=0;j < s.length();j++) { tmp = mymap[s.at(j)]; i = std::max(tmp,i); mymap[s.at(j)] = j; len = std::max(len,(j-i)); } return len; } };
每个数组的初始化为-1表示没有出现重复,它不可能比i的初始值大,如果有重复的,直接覆盖,这样可以不用额外的语句判断是否出现重复。
标题
swiftC
复杂度分析
时间复杂度:O(n)。j将会迭代n次。
空间复杂度:O(m)。m是字符集的长度。
相关文章推荐
- [leetcode-3]Longest Substring Without Repeating Characters
- leetcode 第三题Longest Substring Without Repeating Characters
- LeetCode(3)Longest Substring Without Repeating Characters
- leetcode-3 Longest Substring Without Repeating Characters
- LeetCode:Longest Substring Without Repeating Characters(update)
- [leetcode刷题系列]Longest Substring Without Repeating Characters
- LeetCode|Longest Substring Without Repeating Characters
- LeetCode-Longest Substring Without Repeating Characters
- [LeetCode] Longest Substring Without Repeating Characters
- LeetCode 3 Longest Substring Without Repeating Characters
- leetcode-java.T003_LongestSubstringWithoutRepeatingCharacters 给定一个字符串,找字符中的最大非重复子串
- leetcode3 longest Substring Without Repeating Characters
- LeetCode: Longest Substring Without Repeating Characters
- LeetCode3 Longest Substring Without Repeating Characters
- 【LeetCode】Longest Substring Without Repeating Characters
- leetcode-Length of the longest substring without repeating characters
- leetcode-Longest Substring Without Repeating Characters
- LeetCode 第 3 题(Longest Substring Without Repeating Characters)
- LeetCode 3 Longest Substring Without Repeating Characters
- [Leetcode] #3 Longest Substring Without Repeating Characters