leetcode之路003 Longest Substring Without Repeating Characters

来源:互联网 发布:电子笔记 知乎 编辑:程序博客网 时间:2024/06/05 14:07


题目大意:很好理解,求最长的没有重复元素的子串的长度,例如:“abcabcbb”结果是abc,长度3,“bbbbb”结果时b,长度1.

思路:用hash_map做的,因为每次找下一个字符时,都需要从前面的子串中进行查找操作,利用hash_map达到快速查找。因为不需要要排序操作,因此程序中是用的unordered_map。

1.令最长的无重复子串长度为max,i从0循环到字符串长度减去max

2.对每一个循环,从i开始,指定一个数len来指示从i开始能达到的长度,若从unordered_map hash中没有发现字符s[i],则将s[i]加入hash中,并将len++指向下一个字符,再进行判断是否在unordered_map hash中,依次循环执行。直到unordered_map hash中找到此字符,即重复了。此时len即为从i开始的最大长度。

3.判断2找到的len与max的大小,若len大于max,则更新max。

4.按照最简单的思路,用1-3,一直循环,肯定能求出最大的max。但是,效率是个问题,提交时肯定会出现超时,有一个测试数据,string长度大于31000。在每一次i的循环中,在找到重复元素时,我进行了如下优化:

a.找到hash中那个重复元素的下标,若此下标大于i值,则将此下标值+1赋给i。例:"abcdecmnp",此时c重复,则hash中c的下标为2,大于第一个a的下标0,将i=2+1,因为从0-2的元素开始的子串长度必定小于从a开始的长度。

b.找到hash中那个重复元素的下标,若此下标等于i值,则继续判断下一个元素是否重复。例:“abcdabmn”,i=0,到a重复时,找到的重复下标为0,判断下一个元素b,直到没有发现重复或者进入a中。


优化的策略有点混乱,自己举例测试出来进行尝试的结果,感觉不是很好,还可以优化。提交运行ac了,运行时间为76ms,发现提交的c++代码一般都在16-30ms之间= =。代码如下:

#include<iostream>#include<string>#include <unordered_map>using namespace std;class Solution{public:int lengthOfLongestSubstring(string s){int max=0,len=0;int off=0;for(int i=0;i<s.length()-max;++i){unordered_map<char,int> hash;unordered_map<char,int>::iterator hashit;len=0;while(hash.find(s[i+len])==hash.end()&&(i+len)<s.length()){hash[s[i+len]]=len++;}if(len>max)  max=len;off=0;if(hash.find(s[i+len+off])!=hash.end()){while(hash.find(s[i+len+off])!=hash.end()){hashit=hash.find(s[i+len+off]);if(hashit->second>off){  off=hashit->second+1;  break;}else  ++off;}--off;}i=i+off;}return max;}};int main(){string ss="aab";Solution so;int rr=so.lengthOfLongestSubstring(ss);cout<<rr<<endl;return 0;}
本来想改进一下代码的,结果毫无头绪。看了讨论区的算法思想,写了以下代码:

class Solution{public:int lengthOfLongestSubstring(string s) {unordered_map<char,int> hash;unordered_map<char,int>::iterator hashit;     int longest=0,m=0;     for (int i=0;i<s.length();++i) { if(hash.find(s[i])!=hash.end()) {hashit=hash.find(s[i]);m=max(m,hashit->second+1); }         <span style="white-space:pre"></span>hash[s[i]]=i; longest=max(longest,i-m+1);         } return longest;}};
主要思路也是利用hash_map的快速查找,在hash中找到重复元素后,根据重复元素的下标和原子串的第一个位置下标相比,来更新m的值,已达到更新最长长度longest值的目的。此算法只需要O(n)的时间。并且,只用了一个hash_map就可以完成操作,因为如果“abcacdeb“,比如b值,前面已经存到hash_map中了,但是,此时m值指示的是子串的起始位置为3,因此即使可以找到,但是不会影响长度的计算。

反观自己写的,对每一个i需要进行一个循环,还需要分配hash_map,直到找到重复时才停止循环,虽然有优化可以使i值进行跳跃,不处理长度肯定小于max的情况,但总的说来,复杂了许多。从提交的运行时间,上述代码只需56ms,自己的需要76ms。


2016.09.02更新 JAVA代码

public class Solution {    public int lengthOfLongestSubstring(String s) {if (s == null || s.length() == 0) {return 0;}int res = 1;Queue<Character> q = new LinkedList<>();q.offer(s.charAt(0));Map<Character, Integer> map = new HashMap<>();map.put(s.charAt(0), 0);for (int i = 1; i < s.length(); i++) {if (map.containsKey(s.charAt(i))) {res = Math.max(res, i - map.get(s.charAt(i)));while (!q.isEmpty() && q.peek() != s.charAt(i)) {map.remove(q.poll());}q.poll();map.put(s.charAt(i), i);q.offer(s.charAt(i));} else {map.put(s.charAt(i), i);q.offer(s.charAt(i));res = Math.max(res, map.size());}}return res;}}

优化,只用一个map:

public int lengthOfLongestSubstring(String s) {if (s == null || s.length() == 0) {return 0;}int res = 1;Map<Character, Integer> map = new HashMap<>();int low = -1;for(int i = 0; i < s.length(); i++) {    if(map.containsKey(s.charAt(i))) {        low = Math.max(low, map.get(s.charAt(i)));        map.put(s.charAt(i), i);    } else {        map.put(s.charAt(i), i);    }    res = Math.max(res, i - low);}return res;}









0 0
原创粉丝点击