leetcode之路003 Longest Substring Without Repeating Characters


题目大意:很好理解,求最长的没有重复元素的子串的长度,例如:“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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值