您的位置:首页 > 其它

JZOJ 100046. 【NOIP2017提高A组模拟7.14】收集卡片

2017-07-14 16:36 447 查看

Description

Star 计划订购一本将要发行的周刊杂志,但他可不是为了读书,而是—— 集卡。 已知杂志将要发行 N 周(也就是 N 期),每期都会附赠一张卡片。Star 通 过种种途径,了解到 N 期杂志附赠的卡片种类。Star 只想订购连续的若干期, 并在这些期内收集所有可能出现的种类的卡片。现在他想知道,他最少需要订 购多少期。

Input

第一行一个整数 N;

第二行一个长度为 N 的字符串,由大写或小写字母组成,第 i 个字符表示 第 i 期附赠的卡片种类,每种字符(区分大小写)表示一种卡片。

Output

输出一行一个整数,表示 Star 最少订购的期数。

Sample Input

8

acbbbcca

Sample Output

3

Data Constraint

对于 30%的数据,N ≤ 300;

对于 40%的数据,N ≤ 2000;

对于 60%的数据,N ≤ 5000;

对于 80%的数据,N ≤ 100000;

对于 100%的数据,N ≤ 500000。

Solution

这题的题意就是给你一段包含多种元素序列,求最短的一段连续的区间包含所有种类的元素。

那么就有各种解决的方法了,我的方法是 O(NlogN) 的。(听说 O(N) 扫一遍就可以了)

先二分答案 Mid ,把原问题转化成判定性问题。

在将长为 Mid 的区间一格一格地从左向右移动,一边加进一个新元素,一边删去。

中途用一个标志数组 Bz[i] 表示 i 这个字母在区间中出现的多少次,

并维护包含的元素种类是否达到全部即可,这样的复杂度是 O(N) 。

再结合上二分,总时间复杂度就是 O(NlogN) ,秒过本题。

Code

#include<cstdio>
#include<cstring>
using namespace std;
int n,num;
int a[500001],bz[150];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
char ch=getchar();
while(!('A'<=ch && ch<='Z' || 'a'<=ch && ch<='z')) ch=getchar();
if(++bz[a[i]=ch]==1) num++;
}
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1,k=0;
memset(bz,0,sizeof(bz));
for(int i=1;i<=mid && k<num;i++)
if(++bz[a[i]]==1) k++;
if(k==num)
{
r=mid;
continue;
}
for(int i=mid+1;i<=n && k<num;i++)
{
if(++bz[a[i]]==1) k++;
if(!--bz[a[i-mid]]) k--;
}
if(k==num) r=mid; else l=mid+1;
}
printf("%d",l);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: