zoukankan      html  css  js  c++  java
  • LeetCode3. Longest Substring Without Repeating Characters

    (0)前言

      本来不打算在博客里面记录自己刷LeetCode的经过。做了几道题目以后发现可以AC倒是不假,但是使用的方法在时间效率上平均只能打败50%左右的用户。因而决定还是记录一下,不该小瞧这些基础的题目,它们自有存在的价值。

    (一)题意

    题目链接:https://leetcode.com/problems/longest-substring-without-repeating-characters/

    3. Longest Substring Without Repeating Characters

    Given a string, find the length of the longest substring without repeating characters.

    Examples:

    Given "abcabcbb", the answer is "abc", which the length is 3.

    Given "bbbbb", the answer is "b", with the length of 1.

    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.

    ————————————————————————————————————————————————————————————————————————————

    (二)题解

    (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 };
    LeetCode 3-1

    (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 };
    LeetCode3-2

    但是第一种实现用时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 };
    LeetCode3-3

    (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 };
    LeetCode3-4

    三种方法是一个循序渐进的过程,纯纯思考这个过程对我来说还是有些煎熬的,虽然道理是很显然的,借助图来说明一下,

    第一种方法

    第二种方法

    第三种方法

  • 相关阅读:
    575. Distribute Candies
    242. Valid Anagram
    349. Intersection of Two Arrays
    290. Word Pattern
    389. Find the Difference
    httpModules 与 httpHandlers
    JS获取浏览器信息及屏幕分辨率
    asp.net 页面编码 设置的几种方法
    IIS是如何处理ASP.NET请求的
    VS2010常用插件介绍之Javascript插件(一)
  • 原文地址:https://www.cnblogs.com/xiaozhuyang/p/6119686.html
Copyright © 2011-2022 走看看