题目大意:
题目链接:https://jzoj.net/senior/#main/show/100046
题目图片:
http://wx3.sinaimg.cn/mw690/0060lm7Tly1fy7ghcpx5gj30j50fc0t4.jpg
http://wx4.sinaimg.cn/mw690/0060lm7Tly1fy7ghcpmnaj30j605dt8m.jpg
给出一个长
n
n
n的字符串,求最短连续子串包含了原串中含有的所有字母。
思路:
很明显的双指针模拟。
每次往前移指针
i
i
i,那么自然的可能需要后移指针
j
j
j。
用
n
u
m
[
x
]
num[x]
num[x]表示字母
x
x
x在
s
[
k
]
(
k
∈
[
i
,
j
)
)
s[k](k\in[i,j))
s[k](k∈[i,j))中出现的次数。
s
u
m
sum
sum表示两个指针之间不同字母的个数。
当
s
u
m
=
sum=
sum=总字母数时,那么
[
i
,
j
)
[i,j)
[i,j)就是一个符合要求的子串,长度
j
−
i
+
1
j-i+1
j−i+1。
否则后移指针
j
j
j。
实际上为了简便,可以把字母转换成数字,
1
∼
26
1\sim 26
1∼26分别表示
a
∼
z
a\sim z
a∼z,
27
∼
52
27\sim52
27∼52分别表示
A
∼
Z
A\sim Z
A∼Z。
代码:
#include <cstdio>
#include <iostream>
using namespace std;
const int N=500010;
int n,ans,sum,num[100],a[N],maxn;
char c;
bool vis[100],ok;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
c=getchar();
while ((c<'a'||c>'z')&&(c<'A'||c>'Z')) c=getchar();
if (c>='a'&&c<='z') a[i]=c-'a'+1;
else a[i]=c-'A'+27; //转换成数字
if (!vis[a[i]])
{
maxn++;
vis[a[i]]=1;
}
}
int i=0,j=0;
ans=2147483647;
num[0]=23333;
while (j<=n)
{
num[a[i]]--;
if (!num[a[i]]) sum--; //该数子不在区间[i,j)内了
i++;
while (sum<maxn&&j<=n) //没有包含所有字母
{
j++;
if (j>n)
{
ok=1;
break;
}
if (!num[a[j]]) sum++; //新字母
num[a[j]]++;
}
if (ok) break;
ans=min(ans,j-i+1);
}
printf("%d\n",ans);
return 0;
}
题外话
也可以用前缀和+二分做,时间复杂度 O ( n l o g n ) O(n\ logn) O(n logn)。