Longest Substring Without Repeating Characters

Longest Substring Without Repeating Characters

问题

给定一个字符串,找出最长不重复字符的子串。例如,”abcabcbb”的最长子串是”abc”,长度是3。对于”bbbbb”的最长子串是”b”,长度是1。

解决方案

O(n) runtime, O(1) space – 两次遍历:
我们如何能立即查找一个字符是否在字符串中存在?答案是用一个最简单的表来存储出现过的字符。请和你的面试官确认字符串是否包含除了”a-z”以外的字符(例如:数字?大写字母?它是否只是包含ASCII字符?或者可能有unicode字符?)
接下来一个你需要问自己如果找出了重复字符应该怎么办?例如,如果字符串是”abcdcef”,当你遇见了第二个重复的c的时候应该怎么办?
当你发现一个重复的字符(假设当在j的位置发现的),这就表明当前的字符串(当然除了重复字符)可能是一个潜在的最长不重复子串,所以接下去更新最长的值。那也表示这个重复的字符在之前i的位置也出现过,当然i肯定小于j。
既然你已经知道所有在i或者i之前开始的子串都会小于当前的最大值,你可以安全地开始找从i+1位置开始的子串。
因此,你会需要两个标志位来记录当前子串的头和尾。既然i和j都最多遍历n步,最坏的可能性就是2n步,所以最终的时间复杂度就是O(n)。
注意空间复杂度是O(1),因为即便我们分配了一个数组,但是由于无论多长,这个数组的长度永远是256。

public int lengthOfLongestSubstring(String s) {
    boolean[] exist = new boolean[256];
    int i = 0, maxLen = 0;
    // j从开始到结尾遍历
    for (int j = 0; j < s.length(); j++) {
        // 如果当前的字符已经存在,则设为不存在,然后增加i,直到发现不存在为止
        while (exist[s.charAt(j)]) {
            exist[s.charAt(i)] = false;
            i++;
        }
        // 标记为当前字符存在
        exist[s.charAt(j)] = true;
        // 设当前最长值为当前或者最长值
        maxLen = Math.max(j - i + 1, maxLen);
    }
    return maxLen;
}

如果字符集不仅仅是ascii,而且也包含unicode字符呢?我们可以修改以上方案,用一个Set来代替一个简单的256个元素的boolean数组。
O(n) runtime, O(1) space – 一次遍历:
以上的方案要求最多2n步。其实,我们可以优化成只需要n步。与其用一个table来判断一个字符是否存在,我们可以定义一个字符以及它的位置的mapping。然后我们可以在找到重复字符后,马上跳过字符。

public int lengthOfLongestSubstring(String s) {
    // 定义一个256个字符的数组
    int[] charMap = new int[256];
    // 将charMap中所有的值默认设为-1
    Arrays.fill(charMap, -1);
    int i = 0, maxLen = 0;
    // 从头开始遍历s
    for (int j = 0; j < s.length(); j++) {
        // 如果当前字符的值>=i,也就是说当前字符在i之前的位置出现过
        if (charMap[s.charAt(j)] >= i) {
            // 则从当前字符的位置的后面1位再开始算起
            i = charMap[s.charAt(j)] + 1;
        }
        // 标注当前字符的出现位置
        charMap[s.charAt(j)] = j;
        // 得出当前j-i+1(当前长度),以及最大长度之前的最大值
        maxLen = Math.max(j - i + 1, maxLen);
    }
    return maxLen;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值