【icyle】Leetcode-cn:3.无重复字符的最长子串

题目

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

示例 1:

输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。

示例 2:

输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。

示例 3:

输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

示例 4:

输入: s = “”
输出: 0

解答1

思路

滑动窗口思想。以字符串abcad为例。

  1. 首先,申请一个队列。左指针和右指针同时指向a,a入队。经过两轮循环后,右指针指向c。
字符串:
a b c a d
↑   ↑
l   r
队列:abc
  1. 此时检测下一个字符为a,与前面的a重复了。按照FIFO原理,第一个字符a出队。
字符串:
a b c a d
  ↑ ↑  
  l r
队列:abc
  1. 再检查一遍,此时队中只有bc,不存在与a重复的字符,a可入队。
字符串:
a b c a d
  ↑   ↑  
  l   r
队列:bca
  1. 每入队一次,都检查是否需要修改最大子串长度。
需要的头文件

unordered_set。(c++11新模板)

代码
/*
 * @lc app=leetcode.cn id=3 lang=cpp
 *
 * [3] 无重复字符的最长子串
 */

// @lc code=start

#include 
#include 
#include 
using namespace std;

class Solution
{
public:
    int lengthOfLongestSubstring(string s)
    {
        //异常输入排除
        if (s.size() == 0)
        {
            return 0;
        }
        else if (s.size() == 1)
        {
            return 1;
        }

        // 以哈希表为基础的set
        unordered_set<char> subs;

        // lp为左指针,rp为右指针,subslength为当前子串长度,maxlength为最大子串长度
        int lp = 0, rp = 1, maxlength = 0;

        //第一个字符插入子串
        subs.insert(s[0]);

        while (rp < s.size())
        {
            //如果找到了之前subs中的字符(重复)
            if (subs.find(s[rp]) != subs.end())
            {
                //擦掉最左边的字符,左指针加1
                subs.erase(s[lp]);
                lp++;
            }
            //没有重复的情况
            else
            {
                //当前右指针指向的元素入set
                subs.insert(s[rp]);

                //右指针+1
                rp++;

                //确定一次最大长度
                maxlength = (subs.size() > maxlength) ? subs.size() : maxlength;
            }
        }

        return maxlength;
    }
};

// @lc code=end

时间复杂度和空间复杂度
  • 时间复杂度:遍历一次字符串,所以为 O(N) ,其中 N 是字符串的长度。
  • 空间复杂度:申请了一个set存放字符,所以为 O(N) ,其中 N 是字符串的长度。

解答2

思路

代码已详细阐述。实际上,仍然是滑动窗口思想。

需要的头文件

vector

代码
/*
 * @lc app=leetcode.cn id=3 lang=cpp
 *
 * [3] 无重复字符的最长子串
 */

// @lc code=start

#include 
#include 
#include 
using namespace std;

class Solution
{
public:
    int lengthOfLongestSubstring(string s)
    {
        //定义一个vevtor数组,128大小是为了包括所有的大小写字母
        vector<int> ascii_vector(128, 0);

        //定义最长子串长度变量
        int subslength = 0;

        //定义左指针,一开始指向位置0
        int lp = 0;

        //遍历字符串
        for (int i = 0; i < s.size(); i++)
        {
            /*
            原理:
            ascii_vector:保存了字符串中某个字符最后一次出现时,下一个字符的下标
            ascii_vector初始全为0,遍历数组:
            如果当前ascii码下标处(ascii_vector[s[i]])为0,证明之前没出现过该字符。
            如果当前ascii码下标处不为0,则证明之前已经出现过该字符。
            lpcount:如果检测到重复的字符,应返回之前该重复字符最后一次出现的位置的下个字符的下标。
            */

            // 当出现重复字符时,ascii_vector一定比左指针大,lp就会指向该重复字符最后一次出现的位置的下个字符的下标。
            lp = max(lp, ascii_vector[s[i]]);

            // 既然检测到该字符,那么这个字符就是最后一次出现的位置,将下一个字符下标传进来vector里
            ascii_vector[s[i]] = i + 1;

            //i相当于右指针,lp相当于左指针,subslength每次都会记录最长子串长度
            subslength = max(subslength, i - lp + 1);
        }
        return subslength;
    }
};

// @lc code=end

时间复杂度和空间复杂度
  • 时间复杂度:左指针和右指针分别会遍历整个字符串一次,所以为 O(N) ,其中 N 是字符串的长度。
  • 空间复杂度:在本题中没有明确说明字符集,因此可以默认为所有 ASCII 码在0~128内的字符。所以为 O(X) ,其中X为字符集中字符总个数。

反思与总结

  1. 灵活利用vector数组+ascii编码的特点进行解题。这里就是运用了ascii码实际上可以充当数字下标的特性。
  2. 灵活建立指针帮助解题。这里自行建立了左指针,然后把循环的i当作右指针,方便计算出子串的长度。

你可能感兴趣的:(leetcode,c++)