1. 题目
2. 我的解法
笔者一开始没想用暴力,规避暴力。但是脑子里面没有sliding windows
的概念,一开始的想法是好的,很快实现了一个自认为是
的思路。但是发现不能通过所有的AC,因为题目没有理解清楚,有些test case失败。最后就不断修改,不断debug,实现了一个复杂度为
的算法。
看了推荐的解法,才明白。我想实现的就是sliding windows
,然后我的解法以糟糕的方式实现了sliding windows
的功能。
特别的,C++
笔者仅限于做选择题,可以说之前没有写过一句C++的代码,使用STL map
也是边看边用,可能使用hash_map
要更适应现在的场合。如果使用C语言,就需要自己实现hash table
,虽然在DSAA记录篇,已经实现了open hash或者close hash,但毕竟有违限时做题的前提。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int i,j,max=0,num=0;
char prechar=0;
hash_map<char, char> mp;
for(i=0;i < s.size();++i){
//每次插入之前查找当前是否具有该元素
if(!mp.count(s[i])){
//没有该元素加1,然后更新max,如果max<=num
num++;
//cout <<"update "<<num<<endl;
if(max <=num)
max=num;
}
else{
//从当前开始重新计数
num=1;
//向后遍历看i后面有几个不同的数
for(j=i-1;s[j] != s[i];j--)
num++;
mp.clear();
//重新插入mp
for(j=j+1;j<=i;j++)
mp.[s[j]]=s[j];
//cout<<"duplicate "<<num<<endl;
}
mp[s[i]]= s[i];
}
return max;
}
};
3. 优质解法
- A sliding window is an abstract concept commonly used in array/string problems. A window is a range of elements in the array/string which usually defined by the start and end indices, i.e. [i,j)(left-closed, right-open).
- A sliding window is a window “slides” its two boundaries to the certain direction. For example, if we slide [i,j) to the right by 1 element, then it becomes [i+1,j+1)(left-closed, right-open).
sliding window
可以用于解决数组或者字符串中连续元素间的某种属性问题,窗口头部和尾部可以动态调整以适应问题,而窗的现实方式根据具体问题有不同的方式。本题的特点,需要保证窗体中的元素的唯一性,选取find
和insert
时间复杂度比较小的数据结构都能胜任。
头和尾的是否遵循上面的[i,j)或者[i,j]都可以,笔者倾向后者。
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set<Character> set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {
//这里使用的窗为[i,j],所以j<n
// try to extend the range [i, j]
if (!set.contains(s.charAt(j))){
//这里和笔者之前的逻辑一样
set.add(s.charAt(j++));
//如果j的递增不在上面,下面就应该为(j-1+1),不赘述了
ans = Math.max(ans, j - i);
}
else {
//当前窗体中有重复元素,就前移i,并从窗体中删除i对应的元素
//特别的,这种删除会一直持续到重复元素为止,并包含重复元素。
set.remove(s.charAt(i++));
}
}
return ans;
}
}
针对上面一直迭代删除的缺陷,(一个元素可能while循环两次,时间复杂度为 )如果可以直接覆盖key-value,且直接返回窗体中重复元素对应的index,则优化复杂度为 。解法如下:
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>(); // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i);
}
ans = Math.max(ans, j - i + 1);
//重复的元素直接用新的index覆盖旧的,j+1直接返回旧的重复元素的下一个index
map.put(s.charAt(j), j + 1);
}
return ans;
}
}
4. 总结
针对字符串或者数组问题,可以考虑滑窗的方式。滑窗的实现有多种可能,根据问题选择最合适的数据结构。一般在限时情况下,可能做出 已经很不错了,更具体的优化,时间充足才行。其他使用C++或者Java这些高级语言提供了一些不错的库,至少比C语言做这些题要方便多了。