1. 问题:
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.
题意很好理解,就是给定一个字符串,求这个字符串的“最长不重复子串”
另外题目中也说了,子串和子序列是有区别的,区别在于子串必须下标是连续的,而子序列并没有这个要求。
2. 思路:
什么是优化问题?
优化问题通常都是求一个“最”值,比如这道题的求最长子串,只不过是有约束的,约束就是不重复。
穷举法的时间复杂度太高
任何一个问题,一般都会有最笨的办法,穷举法,比如这道题,可以穷举出所有子串,然后给这些子串打上标记,是否重复,重复则舍弃掉,不重复的话,就和前一个不重复子串比较,如果长度更大,作为当前的结果,最终再返回这个结果。但是一般来说,穷举法的时间复杂度都很高,所以实用性不强。
一般把多项式时间作为一个算法时间复杂度的一个分界点,多项式以下,就认为时间复杂度不高,最好是线性时间或者常数时间。
重复与不重复,很容易想到用哈希表去判断,所以这道题的基本思路就是,用两个指针加一个哈希表,哈希表存储字符和它对应的下标,指针i,j,i用来遍历整个字符串,j用来记录重复字符出现的位置,也就是下一个不重复子串开始的位置,搜索的过程中,一直会维护一个当前找到的最长不重复子串,如果又找到一个不重复子串,长度大于当前的话,就代替当前最大值,知道i遍历到最后一个字符为止,这样做的话,时间复杂度是O(n),开销主要集中在i遍历了整个字符串。
还有一个注意点就是,下一个不重复子串的起点,j的位置,比较容易想到的是
j = map.get(s.charAt(i))+1)
但是对于“abba”这样的用例,当i=3时,如果j按照上述规则,那么j是等于1的,显然这是不对的,j应该是2,所以j应该取当前值和
map.get(s.charAt(i))+1)
里的最大值,(这让我想起了初中的时候,做数学题的时候总会忽略一些情况,每次考试过后总是卧槽,又漏了这种情况,那种情况blahblah...),做算法题也是一样,要把情况考虑全面。
3. 代码:
public class Solution {
public int lengthOfLongestSubstring(String s) {
if(s.length() == 0) return 0;
HashMap<Character,Integer> map = new HashMap<Character,Integer>();
int max = 0;
for(int i = 0,j = 0;i<s.length();i++){
if(map.containsKey(s.charAt(i))){
//下一个不重复子串开始的位置
j = Math.max(j,map.get(s.charAt(i))+1);
}
map.put(s.charAt(i),i);
//记录当前最长不重复子串
max = Math.max(max,i-j+1);
}
return max;
}
}