C1000 完美子串题解

Description

\(yyc\) 非常喜欢完美的字符串,当然,一个完美的字符串一定是简约并且多样的。现在 \(yyc\) 有一个字符串,他想知道这个字符串的完美子串的最小长度是多少。
一个完美子串,是原串的一个连续子序列,并且包含了不同的 \(26\) 个大写字
母。
当然了如果 \(yyc\) 的字符串中并不存在完美子串,则输出 \(QwQ\)


设字符串长度为 \(n\)

40pts

先考虑最暴力的暴力,枚举一个长度 \(L\),然后从左往右做一个“滑动窗口”,动态维护一个 \(26\) 大小的桶即可。如果一旦发现合法位置,输出当前的 \(L\) 并退出程序即可。时间复杂度 \(O(n^2)\)

100pts

转换一下暴力,改为枚举一个右端点 \(r\),然后从 \(r\) 开始往左扫,一旦发现已经扫过的区域内包含了所有字母,就把 \(r-l+1\)\(\min\) 然后 \(\operatorname{continue}\)。最后输出 \(\min\) 即可。
显然这样做没有优化时间复杂度,非常容易被卡到 \(n^2\)。但我们发现,假设当前的右端点为 \(r\),那我们在右端点为 \(r-1\) 时已经扫出了一段合法的区间,这段区间完全可以现在继续使用,而不是扔掉不管。
想象现在我们有一段包含 \(26\) 个英文字母的合法区间,往右“添加”一个字母,肯定不会让这个区间变得不合法。于是我们就成功地把 \(r-1\) 时找到的区间转移给了 \(r\)。但这样有可能不是最优。也许在向右“添加”后,左侧可以“吐出”一个字母,且保证区间合法。
所以用双指针做即可。每次让右端点 \(+1\),然后维护左端点,取 \(\min\) 即可。注意边界情况。

#include <bits/stdc++.h>
#include <bits/extc++.h>
#define INF 0x7fffffff
#define MAXN 2000005
#define eps 1e-9
#define foru(a,b,c)	for(int a=b;a<=c;a++)
#define RT return 0;
#define db(x)	cout<<endl<<x<<endl;
#define LL long long
#define int LL
#define LXF int
#define RIN rin()
#define HH printf("\n")
using namespace std;
inline LXF rin(){
	LXF x=0,w=1;
	char ch=0;
	while(ch<'0'||ch>'9'){ 
	if(ch=='-') w=-1;
	ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
	x=x*10+(ch-'0');
	ch=getchar();
	}
	return x*w;
}
string s;
int v[500],tot;
signed main(){
	cin>>s;
	int n=s.size();
	int l=0,r=0;
	v[s[0]]++;
	tot++;
	int ans=INF;
	while(1){
		if(tot==26){
			ans=min(ans,r-l+1);
		}
		if(r<=s.size()-2){			
			r++;
			v[s[r]]++;
			if(v[s[r]]==1)	tot++;
		}
		if(tot==26){
			ans=min(ans,r-l+1);
		}
		while(l<r){
			if(v[s[l]]==1)	break;
			v[s[l]]--;
			l++;
		}
		if(tot==26){
			ans=min(ans,r-l+1);
		}
		if(r==s.size()-1){
			while(l<r && tot==26){
				ans=min(ans,r-l+1);
				v[s[l]]--;
				if(v[s[l]]==0)	tot--;
			}
			break;
		}
	}
	if(ans!=INF)	cout<<ans;
	else	cout<<"QwQ";
	return 0;
}
posted @ 2023-02-21 14:59  Cap1taL  阅读(120)  评论(0)    收藏  举报