(0)前言算法
原本不打算在博客裏面記錄本身刷LeetCode的通過。作了幾道題目之後發現能夠AC卻是不假,可是使用的方法在時間效率上平均只能戰勝50%左右的用戶。於是決定仍是記錄一下,不應小瞧這些基礎的題目,它們自有存在的價值。數組
(一)題意數據結構
題目連接:https://leetcode.com/problems/longest-substring-without-repeating-characters/ide
Given a string, find the length of the longest substring without repeating characters.優化
Examples:spa
Given "abcabcbb"
, the answer is "abc"
, which the length is 3.code
Given "bbbbb"
, the answer is "b"
, with the length of 1.blog
Given "pwwkew"
, the answer is "wke"
, with the length of 3. Note that the answer must be a substring, "pwke"
is a subsequence and not a substring.leetcode
————————————————————————————————————————————————————————————————————————————字符串
(二)題解
(1)普通解法-O(n2)
把全部的狀況都枚舉一遍,尋找最長不含重複字母的字串。藉助一個bool數組,快速判斷是否存在重複。代碼以下:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int len = s.length(); 5 bool vis[200]; 6 int ans = 0,sublen; 7 for(int i = 0; i < len; i++) 8 { 9 memset(vis,0,sizeof vis); 10 sublen = 0; 11 for(int j = i; j < len; j++) 12 { 13 if(!vis[s[j]]) 14 { 15 vis[s[j]] = 1; 16 sublen++; 17 } 18 else 19 { 20 break; 21 } 22 23 } 24 if(sublen > ans) ans = sublen; 25 } 26 return ans; 27 } 28 };
(2)優化算法1-O(2n)
上面那個解法是最普通的想法。能夠解決這個問題,可是效率不高,由於每一次j都是從i逐個日後移動,而i也是逐個移動的,這致使進行了不少次重複的判斷。
而實際上在上一輪中已經作過判斷的子串能夠繼續加以利用。因而引入了set的數據結構。這種方法叫作滑動窗口法
set是集合,包含的元素相互都是不重複的。
於是只要掃一遍字符串就能夠解決問題了,可是i和j最壞狀況下會重複掃描了同一個字符,因此時間複雜度是2n。
代碼以下:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int len = s.length(); 5 set<char> sub; 6 int i = 0, j = 0,ans = 0; 7 //int sublen = 0; 8 while(i < len && j < len) 9 { 10 if(sub.find(s[j]) == sub.end()) 11 { 12 sub.insert(s[j]); 13 j ++; 14 if((j - i) > ans) 15 ans = j - i; 16 } 17 else 18 { 19 sub.erase(sub.find(s[i])); 20 i++; 21 } 22 } 23 return ans; 24 } 25 };
可是第一種實現用時36ms,第二種實現用時132ms。我懷疑是set的引入花費了時間。set要用O(logn)的時間查找、刪除、增長元素。
因此改爲了用hash的方法,使用bool類型的vis數組標記字符是否出現過.
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int len = s.length(); 5 int i = 0, j = 0,ans = 0; 6 bool vis[300]; 7 memset(vis,0,sizeof vis); 8 while(i < len && j < len) 9 { 10 if(!vis[s[j]]) 11 { 12 vis[s[j]] = 1; 13 j++; 14 } 15 else 16 { 17 vis[s[i]] = 0; 18 if(j - i > ans) 19 ans = j - i; 20 i++; 21 } 22 } 23 if(j - i > ans) 24 ans = j - i; 25 return ans; 26 } 27 };
(3)優化算法2-O(n)
是對滑動窗口的一種優化,不只記錄已經判斷過的元素,還記錄該元素對應的下標。
若是j’是與j位置相同的字符,那麼下一次i只須要從j’+1開始判斷便可,不須要重複[i+1,j']之間的這些判斷。
代碼以下:
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 cout<<endl; 5 int len = s.length(); 6 int i = 0, j = 0,ans = 0; 7 int vis[300]; 8 for(int k = 0; k < 300; k++) 9 vis[k] = -1; 10 while(i < len && j < len) 11 { 12 if(vis[s[j]] == -1 || vis[s[j]] < i) 13 { 14 } 15 else 16 { 17 if(j - i > ans) 18 ans = j - i; 19 i = vis[s[j]] + 1; 20 } 21 vis[s[j]] = j; 22 j++; 23 } 24 if(j - i > ans) 25 ans = j - i; 26 return ans; 27 } 28 };
三種方法是一個按部就班的過程,純純思考這個過程對我來講仍是有些煎熬的,雖然道理是很顯然的,藉助圖來講明一下,
第一種方法
第二種方法
第三種方法