LeetCode3:Longest Substring Without Repeating Characters

来源:互联网 发布:linux识别不到hba卡 编辑:程序博客网 时间:2024/05/16 06:29

Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for “abcabcbb” is “abc”, which the length is 3. For “bbbbb” the longest substring is “b”, with the length of 1.

解法

这道题最直观的做法是从每一个元素开始,寻找以它为起始的无重复的字符串,但是时间复杂度度是O(N^3),明显不符合要求。它大量的时间都花在了比较上面,那么可以朝这个方向想,如果将比较的时间变少,这样算法的时间复杂度就能降低。

我们知道stl中map和set在对键的比较是非常迅速的,时间复杂度是O(logN),那么可以考虑到使用map或set。

最开始考虑的是使用一个set,遍历字符串string中的元素,首先判断字符是否在set中,如果不在,那么将这个字符插入set中;如果在set中,表示这个字符在前面已经出现了,此时表示出现了重复字符,那么需要做的事情是将这个重复字符和这个重复字符之前的元素从set中删除掉,然后再将这个元素插入set中,每次插入元素后都对最长的无重复字符进行更新,将它和set中元素的个数进行比较,实际上set中存放的就是最长的无重复的字符。如下图:
这里写图片描述

这种方法有一个需要考虑的问题就是我们可以查询到set中之前插入的b,但是却无法知道b之前插入的字符有那些,因为插入set中的元素是自动按键进行排序的,如果要知道插入b之前插入了那些字符,还需要借助之前的字符串,我们可以在字符串中找到b然后寻找b之前插入的字符,然后在set中将这些字符删除掉,注意此时在字符串中查找字符时使用find函数需要提供第二个参数,就是查询的起始位置,如下图:

这里写图片描述

处于效率的原因,可以使用unordered_set而不是set。
runtime:72ms

 int lengthOfLongestSubstring(string s) {        unordered_set<char> tables;        int first=0;        int result=0;        for(int i=0;i<s.size();i++)        {            //如果在hash表中找到了这个字符,需要删除一些元素            if(tables.count(s[i]))            {                int last=s.find(s[i],first);//注意find函数的第二个参数                for(int j=first;j<=last;j++)                {                    tables.erase(s[j]);                }                first=last+1;                tables.insert(s[i]);                result=max(result,(int)tables.size());            }            else//否则将它插入hash表中            {                tables.insert(s[i]);                result=max(result,(int)tables.size());            }        }        return result;    }

优化一

上面的代码其实还可以做一个很大程度的优化,因为有个双层for循环。为什么需要里面的for循环?因为从set中无法得知要删除的那些元素之前有那些元素,所以我们需要从原始的字符串中获取重复的那个元素之前还有那些元素,这里就又多了一个循环。但是我们可以使用另外一种数据结构map,然这个map的值保持字符的下标,那么看看能不能避免这个for循环?

如果使用map,可以让map的键存放字符,map的值存放下标,并且用一个变量来表示无重复字符串的起始下标,这个下标应该设置为0。那么遍历字符串时判断字符是否在map中出现过,如果出现过,那么就需要这个用来表示起始下标的变量更新为在map中出现过的那个字符的下标的下一位。然后更新这个字符出现的下标为新的小标,最后每一步都需要计算最长的无重复字符的长度。

这里写图片描述

runtime:56ms

  int lengthOfLongestSubstring(string s) {        unordered_map<char,int> tables;        int start=0,result=0;        for(int i=0;i<s.size();i++)        {            if(tables.find(s[i])!=tables.end())                start=max(start,tables[s[i]]+1);            tables[s[i]]=i;//直接利用map[]运算符的特征,它将覆盖键为s[i]对应的值            result=max(result,i-start+1);        }        return result;    }

优化二

由于字符串中的字符种类是小于256个的,所以可以直接使用数组而不是使用map,思路和上面一样,都是定义一个变量来保存无重复字符串的第一个字符的下标,如果碰到了重复的字符,那么更新这个变量。

虽然只是一个小小的改进,但是程序的执行时间缩短了很多,从中也可以看出使用stl的效率问题。

runtime:16ms

int lengthOfLongestSubstring(string s) {        int tables[256];        fill(tables,tables+256,-1);        int result=0,lastRepeatedPos=-1;        for(int i=0;i<s.size();i++)        {            //当找到重复元素并且lastRepeatedPos小于找到的重复元素的下标时更新lastRepeatedPos的值            if(tables[s[i]]!=-1&&lastRepeatedPos<tables[s[i]])                lastRepeatedPos=tables[s[i]];            //更新最大值            result=max(result,i-lastRepeatedPos);            //将元素插入表中            tables[s[i]]=i;        }        return result;    }
0 0
原创粉丝点击