无重复字符最长子串

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

 

示例 1:

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


方法一
基本思路:滑动窗口

什么是滑动窗口?

其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!

如何移动?

我们只要把队列的左边的元素移出就行了,直到满足题目要求!

一直维持这样的队列,找出队列出现最长的长度时候,求出解!

作者:powcai
链接:https://leetcode.cn/problems/longest-substring-without-repeating-characters/solutions/3982/hua-dong-chuang-kou-by-powcai/
来源:力扣(LeetCode)

int que_find(char *que, char *s, int len)    //遍历数组,寻找是否存在与*s相同的元素
{
    for(int i = 0; i < len; i++)
    {
        if(*s == *(que + i))
            return 1;
    }
    return 0;
}


int lengthOfLongestSubstring(char * s){
     
    int len = strlen(s);
    char *que = malloc(sizeof(char)*len);    //注意malloc()分配的使用,直接使用malloc(sizeof(s))会产生下图报错信息
    int max_len=0, cur_len=0;
    int end = 0, start = 0;
    for( long int  i =0; i< len; i++)
    {
        cur_len += 1;
        while(que_find(que, s+i, len))    //注意这个循环,它的作用是循环将与*(s + i)相同的元素删除
        {
            *(que + start) = 0;       //delete
            start += 1;
            cur_len -= 1;
        }
        *(que + end) = *(s + i);        //元素入队
        end += 1;
        if(cur_len > max_len)          //记录最长子串
            max_len = cur_len;

    }

    return max_len;
}

 滑动窗口算法主要应用对象:数组和字符串

结合滑动窗口算法的说明和本例题,可知滑动窗口算法可以用来解决一些查找满足一定连续区间的性质(长度)的问题,

因此当区间发生了变化(不满足给定条件)时,可以通过旧有的计算结果(窗口)对搜索空间进行剪枝,从而达到减低时间复杂度的效果

类似于“找到满足 xx 的最 x 的区间(子串、子数组)的 xx”的问题,都可以使用滑动窗口解决。

注意:滑动窗口是一种思想、算法,不用拘泥于数据结构。(不一定要用队列求解本例)

滑动窗口基本框架 

  • 滑动:窗口是移动的,方向是我们根据要求自己设定
  • 窗口:窗口大小不一定是固定的,它可以随时改变,根据需要扩大(直到满足一定条件)或缩小(直到找到一个满足条件的最小窗口);也能保持固定        

 结合本题,深入理解滑动窗口算法,并将其抽象

  1. 我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个「窗口」
  2. 我们先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的字符串符合要求(无重复字符)
  3. 此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的字符串不再符合要求(直到与*(s+i)无重复)。同时,每次增加 left,我们都要更新一轮结果。

  4. 重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头

以下是更高效的程序例程

int lengthOfLongestSubstring(char * s)
{
    
    int len = strlen(s);

    if (len <= 1) {
        return len;
    }

    char *left = &s[0];
    char *right = &s[0];    //滑动窗口左右指针
    int maxLen = 0;
    int map[256] = {0};
    memset(map, 0, sizeof(map));
    for (int i = 0; i < len; i++) {
        map[*right]++;        //根据每个字符对应一个ASCII码,每次遍历对应键的值就加1(类似于哈希表)

        while(map[*right] > 1) {  //超过1,说明字符重复,左指针右移
            map[*left]--;
            left++;
        }

        right++;

        maxLen = maxLen < (right - left) ? (right -left) : maxLen;
    }

    return maxLen;
}

 

 

 

 

附:

 参考文章:滑动窗口算法基本原理与实践 - huansky - 博客园 (cnblogs.com)

posted @ 2023-03-09 17:01  哈啰世界  阅读(17)  评论(0编辑  收藏  举报