[算法] 滑动窗口算法--查找最长子串

滑动窗口

  滑动窗口也叫尺取法,是一种在字符串或数组中查找满足条件的算法。
  例如可以在字符串中查找满足条件的子串。

滑动窗口图示

  在这里,规定窗口大小为3,每次向右滑动一位。
  当然,窗口的大小可以随意调整,调整至满足条件即可。
在这里插入图片描述

2. 利用滑动窗口查找最长子串

基本原理

思路一:

  规定开始窗口大小为0,利用左右指针指向子串的开始和结束。截取左右指针中的子串,每次移动右边指针将元素加入子串并判断在子串中是否有相同元素,如果有则移动左指针。一直重复上述操作,直到右指针指向字符串尾。记录出现的最长子串。
在这里插入图片描述
代码:

// 判断右指针元素是否在前方出现,没有出现则扩大窗口大小,元素出现则改变左指针位置
// 此方法利用滑动窗口将窗口的字符串截取出来存到子串中,判断子串中是否包含即将加入的字符串,
int lengthOfLongestSubstring(string s) {
    if (s.length() <= 1)
    {
        return s.length();
    }

    int left = 0, right = 0;
    int maxlen = 0;

    while (right <= s.length())
    {
        // 截取子串
        string sub = s.substr(left, right - left);
        maxlen = maxlen > right - left ? maxlen : right - left;
        
        // 判断子串中是否含有新加入的字符,如果含有新加入的字符,改变窗口大小
        while ( left < right && sub.find(s[right]) != string::npos)
        {
            left++;
            // 窗口大小发生改变 截取新串
            sub = s.substr(left, right - left);
        }

        right++;
    }

    return maxlen;
}

思路二:

  原理与上述相同,但不用截取子串,效率更高。

int lengthOfLongestSubstring(string s) {
	if (s.length() <= 1)
	{
	    return s.length();
	}

    int left = 0, right = 1, max = 1;

    while (right < len)
    {
        // 在子串中进行查找该字符第一次出现的位置,如果第一次出现的位置与右值相同,则代表没有出现过该字符
        while (right < len && s.find(s[right], left) == right)
        {
            right++;
        }
        max = right - left > max ? right - left : max;
        left++;
    }
    return max;
}

思路三:

  与上述思路完全不同,不再利用滑动窗口。出现一个字符记录一个字符,并且累加器累加,如果遇到已经出现过的字符,则根据这个重复字符出现在字符串中的位置,决定累加器的值。重复上述操作,记录最大累加器的值。

int lengthOfLongestSubstring(string s) {
    if (s.length() <= 1)
    {
        return s.length();
    }

    int acsii[256] = { 0 };     // 记录字符串出现的次数
    int count = 0;
    int maxcount = 1;

    for (int i = 0; i < s.length(); i++)
    {
        // 如果字符已经出现
        if (acsii[s[i]] == 1)
        {
            // 判断前后时候是一样的字符串,如果重复,则当前字符为新子串,则让计数器重新计数
            if (s[i] == s[i - 1])
            {
                //如果计数器大于字符串一半长度则说明不会出现更长子串, 所以清零也就没有必要了
                if (count > s.length() / 2)
                {
                    break;
                }
                count = 0;
            }  
            else
            {
                // 如果该串与周边字符串没有关系,则对长度-1,再开始计数,
                count--;  
            }  
        }
       
        // 如果没有字符出现则累加器++,如果有字符出现过则对长度改变后再++
        count++;
        acsii[s[i]] = 1;
        maxcount = maxcount > count ? maxcount : count;
    }

    return maxcount;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值