基准时间限制: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;
}
另外本题还可以扩展求一个字符串中包含自定义字符串的最短子串?亦或是求出所有包含自定义字符串的子串?