【算法刷题】无重复字符的最长子串

本文为个人解题思路整理,水平有限,有问题欢迎交流


概览

第一次解出来没花多长时间,但是提交后发现击败了30%的人,也就是意味着还有大幅度优化的空间,于是再优化了一下

难度:中等

核心知识点:滑动窗口 + 贪心


题目来源

力扣:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/


题目内容

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度


样例

输入1

pwwkew

输出1

3

解释1

最长无重复为wke


解题思路

明显这是一个滑动窗口问题,即一个长度为L的窗口从左往右滑动,找到符合情况的最大L,下面进行思路分析

  • 我们需要确定目标的长度,那么需要知道头head和尾tail,并确保中间的所有元素不重复

  • 对于一个子串,如果存在重复元素,那么包含这个子串的任何其他子串也存在重复元素

  • 贪心:即尽可能追求更多,若一个子串满足要求,那么考虑这个子串与相邻的字符能否组成更大的子串,进而将子串扩充,子串越大越好,因此得到的结果是对于每个起点,只有一个最远的终点,即每个起点对应最多一个结果

    比如子串p符合要求,那么考虑pw也符合要求,那么考虑pww不符合要求,则已p开头的最长子串是pw,不可继续贪心

  • 滑动:前面说了pww的最长子串是pw,那么对于pww****呢?*代表暂时未知的字符,如果后面的结果比pw长呢?那么我们也需要搜索后面的字符串,但是两个w不能重复,于是不得不放弃一个w

    • 放弃后一个的结果就是pw
    • 放弃前一个的结果是solve("w****")resolve表示解决方法

    也就是说 solve("pww****") = max(resolve("pw"), soleve("pw****"))

    在对soleve("pw****")同样的方法处理下去,即可拿到最终的最大值

    比如对于目标字符串abcadefa,处理步骤如下(偷个懒,resolve省掉啦,不然看上去很乱)

    //第一次处理,两个a不同,必须放弃其中一个
    solve("abcdadfa")=max(solve("abcd"),solve("bcdadfa"));
    solve("abcd")=4;
    //第二次处理,两个d不同
    solve("bcdadfa")=max(solve("bcda"),solve("adfa"));
    solve("bcda")=4;
    //第三次处理,两个a不同
    solve("adfa")=max(solve("adf"),solve("dfa"));
    solve("adf")=3;
    solve("dfa")=3;
    //那么最终结果为4
    solve("abcdadfa")=max(4,max(4,max(3,3)))=max(4,4,3,3)=4
    

    即从左往右贪心出最长的不重复字符串,直到出现一个与已有字符重复的目标,就贪不动了

    这时候考虑放弃已有字符的和放弃新字符两种情况,取两种情况的结果最大的那个

    • 放弃新字符,即当前子串的长度就是结果,我们一直在保证没有重复字符嘛
    • 放弃旧字符,则从旧字符的下一位开始下一步检索,对新的目标重复贪心和处理

解决方案

  1. 设置两个指针 left 和 right ,初识均指向字符串的头部,设置初始最大长度ans = 0

  2. 记录right目标的字符和位置,right 右移,直至其目标字符已出现过,开始处理

    1. 放弃新字符,则旧字符串的长度为 right-left,修改ans = max(ans, right - left)
    2. 放弃旧字符,若旧字符位置的oldPosition,则 left 修改为oldPosition + 1

    即得到新的字符串

  3. right 继续右移,重复第二步

完整代码

这里使用数组存储位置,而非map,因为字符可按ASCII码存储,显然一个int[128]是比map的效率更高

package com.company;

import java.util.*;

/**
 * @title 无重复字符的最长子串
 * @description 力扣3
 * @author Echo_Ye
 * @date 2020/9/22 11:56
 * @email [email protected]
 */
public class LengthOfLongestSubstring {
    public static void main(String[] args) {
        new LengthOfLongestSubstring();
    }

    public LengthOfLongestSubstring() {
        int ans = lengthOfLongestSubstring("abcdadfa");
        System.out.println(ans);
    }

    public int lengthOfLongestSubstring(String s) {
        int ans = 0;
        int left = 0, right = 0;
        int[] map = new int[128];
        Arrays.fill(map, -1);
        while (right <= s.length()) {
            if (right == s.length()) {
                //结算,结束查找
                ans = max(ans, right - left);
                break;
            }
            //检查right目标
            if (left == right || map[s.charAt(right)] == -1 || map[s.charAt(right)] < left) {
                //存储位置
                map[s.charAt(right)] = right;
                //指针重叠的时候,右指针右移1
                //右指针目标未出现过的时候,右指针右移1
                right++;
            } else {
                //结算,left移至旧字符右1位置
                ans = max(ans, right - left);
                left = map[s.charAt(right)] + 1;
            }
        }
        return ans;
    }

    int max(int a, int b) {
        return a > b ? a : b;
    }
}

结果

【算法刷题】无重复字符的最长子串_第1张图片

性能

【算法刷题】无重复字符的最长子串_第2张图片

后记

没有涉及什么高深的算法,就是思路灵活转换而已,好吧其实涉及到高级算法我可能就不会做了哈哈哈哈哈


作者:Echo_Ye

WX:Echo_YeZ

Email :[email protected]

个人站点:在搭了在搭了。。。(右键 - 新建文件夹)

你可能感兴趣的:(算法,算法,java,leetcode)