欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-25 09:36:58
...

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


概览

第一次解出来没花多长时间,但是提交后发现击败了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;
    }
}

结果

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

性能

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

后记

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


作者:Echo_Ye

WX:Echo_YeZ

Email :[email protected]

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