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.
思路:要找最长的不包含重复字符的子串。设置两个指针left和right,分别指向当前不包含重复字符的子串的首尾,依次扫描原字符串的每个字符,right一直向前走,当碰到重复字符时,就需要改变left的指向,即该重复字符上次出现位置的下一个位置。
如何判断找到重复字符呢?很容易想到要用hash表,所以,一开始想到的算法如下:
void resethash(char *left, char *right, int *hash, char *src) { int i; for(i = 0; i < 128; i++) { hash[i] = -1; } while(left < right) { hash[*left] = left - src; left++; } } int lengthOfLongestSubstring(char* s) { int hash[128] = {}; char *left, *right; int i, j; int res = 0; for(i = 0; i < 128; i++) { hash[i] = -1; } for(left = right = s, i = 0; s[i] != ' '; i++, right++) { if(hash[*right] != -1) { if(res < right - left) { res = right - left; } j = hash[*right]; left = s + j + 1; resethash(left, right, hash, s); } hash[*right] = i; } if(res < right - left) { res = right - left; } return res; }
该算法在leetcode上的测试时间为36 ms。在hash表中记录每个字符的索引,当碰到重复字符时,重置该hash表,也就是该hash表只记录不包含重复字符子串的索引。这是可以优化的地方。
举个例子:str=”abcdefgda”,当扫描到第二个’d’时,查看当前hash表发现有重复字符,因此需要把left指向’e’,right继续向后走扫描到’a’。因为’a’在之前也出现过,但是此时left已经指向了’e’,所以需要把left之前的字符都从hash表中抹去,表明当前得到的子串已不再包含’a’了,从而在第二次遇见’a’时就可以接纳它。
因为hash表记录了每个字符的索引,所以完全可以利用left指针,而无需重置hash表。查询hash表,如果得到当前字符的索引在left之前,就表明当前的非重复子串可以接纳该字符。新算法的运行时间为8ms,算法如下:
int lengthOfLongestSubstring(char* s) { int hash[128] = {}; char *left, *right; int i, j; int res = 0; for(i = 0; i < 128; i++) { hash[i] = -1; } for(left = right = s, i = 0; s[i] != ' '; i++, right++) { if(hash[*right] >= left-s) { left = hash[*right] + 1 + s; } if(res < right - left + 1) { res = right - left + 1; } hash[*right] = i; } return res; }