3. Longest Substring Without Repeating Characters

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(子串).


解法:Sliding Window

In the naive(天真的) approaches(方法), we repeatedly check a substring(子串) to see if it has duplicate(复制的) character. But it is unnecessary. If a substring s_{ij}sij from index ii to j - 1j1 is already checked to have no duplicate(复制的) characters. We only need to check if s[j]s[j] is already in the substring(子串) s_{ij}sij.

To check if a character is already in the substring(子串), we can scan(扫描) the substring, which leads to an O(n^2)O(n2) algorithm(算法). But we can do better.

By using HashSet as a sliding window, checking if a character in the current can be done in O(1)O(1).

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)[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)[i,j) to the right by 11 element(元素), then it becomes [i+1, j+1)[i+1,j+1) (left-closed, right-open).

Back to our problem. We use HashSet to store the characters in current window [i, j)[i,j) (j = ij=i initially). Then we slide the index jj to the right. If it is not in the HashSet, we slide jj further. Doing so until s[j] is already in the HashSet. At this point, we found the maximum size of substrings(子串) without duplicate(复制的) characters start with index ii. If we do this for all ii, we get our answer.


通过用HashSet作为sliding window,我们可以将检查一个字符是否在当前的集合中的时间复杂度降为O(1)。

sliding window是一个通常用来解决数组或字符串问题的抽象概念。

window是通常用start和end下标(索引)定义数组或字符串中元素的范围。sliding window是朝一个确定的方向移动它的两个边界。


在这个问题中我们用HashSet来存储存在于当前window的字符。

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) {
            // try to extend the range [i, j]
            if (!set.contains(s.charAt(j))){
                set.add(s.charAt(j++));
                ans = Math.max(ans, j - i);
            }
            else {
                set.remove(s.charAt(i++));
            }
        }
        return ans;
    }
}

时间复杂度:


O(2n) = O(n)
O(2n)=O(n)
. In the worst case each character will be visited twice by ii and jj.


上述解法中最大的问题在于我们的window的左边界在移动的时候需要一个一个的移动,就是HashSet中需要删除一些元素的时候,需要一个个的移除。

因此我们当然可以想到什么样的数据结构可以让我们的window的左边界可以哗啦一声瞬间移动到指定的位置。因此HashMap就应用而生了。

The above solution(解决方案) requires at most 2n steps. In fact, it could be optimized to require only n steps. Instead of using a set to tell if a character exists or not, we could define(定义) a mapping of the characters to its index. Then we can skip the characters immediately when we found a repeated character.

The reason is that if s[j]s[j] have a duplicate(复制的) in the range [i, j)[i,j) with index j'j, we don't need to increase ii little by little. We can skip all the elements(基础) in the range [i, j'][i,j] and let ii to be j' + 1j+1 directly.

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);
            map.put(s.charAt(j), j + 1);
        }
        return ans;
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值