<Leetcode> Longest Substring Without Repeating Characters

1. 题目

这里写图片描述

2. 我的解法

  笔者一开始没想用暴力,规避暴力。但是脑子里面没有sliding windows的概念,一开始的想法是好的,很快实现了一个自认为是 O ( n ) 的思路。但是发现不能通过所有的AC,因为题目没有理解清楚,有些test case失败。最后就不断修改,不断debug,实现了一个复杂度为 o ( n 3 ) 的算法。
  看了推荐的解法,才明白。我想实现的就是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可以用于解决数组或者字符串中连续元素间的某种属性问题,窗口头部和尾部可以动态调整以适应问题,而窗的现实方式根据具体问题有不同的方式。本题的特点,需要保证窗体中的元素的唯一性,选取findinsert时间复杂度比较小的数据结构都能胜任。
  头和尾的是否遵循上面的[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循环两次,时间复杂度为 O ( 2 n ) )如果可以直接覆盖key-value,且直接返回窗体中重复元素对应的index,则优化复杂度为 O ( n ) 。解法如下:

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. 总结

  针对字符串或者数组问题,可以考虑滑窗的方式。滑窗的实现有多种可能,根据问题选择最合适的数据结构。一般在限时情况下,可能做出 O ( k n ) 已经很不错了,更具体的优化,时间充足才行。其他使用C++或者Java这些高级语言提供了一些不错的库,至少比C语言做这些题要方便多了。

猜你喜欢

转载自blog.csdn.net/lovestackover/article/details/80279939