最短的包含字符串 之 尺取法 O(n)

使用尺取法求解最短的包含所有26个字母的子串,先找到包含所有字母的子串并记录每个字母出现次数,然后逐步调整左右边界寻找最小子串。当达到最小长度26时可提前结束。尺取法常用于求解有限制条件的区间问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基准时间限制:1 秒 空间限制:131072 KB 分值: 20  难度:3级算法题


给出一个字符串,求该字符串的一个子串S,S包含A-Z中的全部字母,并且S是所有符合条件的子串中最短的,输出S的长度。如果给出的字符串中并不包括A-Z中的全部字母,则输出No Solution。
Input
第1行,1个字符串。字符串的长度 <= 100000。
Output
输出包含A-Z的最短子串长度。如果没有符合条件的子串,则输出No Solution。
Input示例
BVCABCDEFFGHIJKLMMNOPQRSTUVWXZYZZ
Output示例
28


先搜索到包含26个字母的子串,确定右边界,同时记录每个字母在子串中出现的个数。

然后确定左边界,左边界为重复的字母 即 该子串可缩短。直到不可缩即出现一个最小子串,保存该长度。

遍历完整个字符串,即可获得最小长度。

必然大于等于26. 当已有最小长度为26时其实可以提前结束了。

尺取法:

    顾名思义像尺子一样一段一段取。通常是对目标数组保存一对下标(即所选取的区间的左右端点),然后根据实际情况不断地推进区间的左、右端点以得出答案。

一般用于求取有一定限制的区间个数或最短的区间等~

#include <iostream>
#include <algorithm>
#include <cstring>
#define NMAX 500005
#define INF 0x3f3f3f3f
using namespace std;
string s;
int a[26];          //字母表
int st, pos, cnt;   //cnt 记录字母表是否到位26
int ans;            //包含字符数
int main(){
    while (cin>>s){
        memset(a, 0, sizeof(a));
        st = pos = cnt= 0;
        ans = s.length() + 1;
        while (pos < s.length() ){
            int t = s[pos++]-'A';   //扩右边界
            if(!a[t]){      //新字母
                cnt++;
            }
            a[t]++;
            if (cnt == 26){ //到位
                while (a[ s[st]-'A' ] > 1){ //缩左边界
                    a[ s[st]-'A' ] --;
                    st++;
                }
                if (ans > pos - st){
                    ans = pos - st;
                }
            }
        }
        if (ans < s.length() + 1 )
            cout<<ans<<endl;
        else
            cout<<"No Solution"<<endl;
    }
    return 0;
}

另外本题还可以扩展求一个字符串中包含自定义字符串的最短子串?亦或是求出所有包含自定义字符串的子串?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值