P6878 [JOI 2020 Final] JJOOII 2 题解

Problem Link

Explanation

给定一个只包含 J\tt JO\tt OI\tt I 三种字符、长度为 NN 的字符串 SS 和一个正整数 KK。定义 KKJOI\tt JOI 串为由恰好 KKJ\tt JKKO\tt OKKI\tt I 依次拼接而成的字串。如 22JOI\tt JOI 串为 JJOOII\tt JJOOII 。你可以对 SS 进行任意次以下操作以将 SS 变为 KKJOI\tt JOI 串:

  1. 删除 SS 的第一个字符
  2. 删除 SS 的最后一个字符
  3. 删除 SS 的任意一个字符

要求最小化并输出第三种操作的次数。如果不能将 SS 变为 KKJOI\tt JOI 串,输出 -1

Solution

可以发现只要定位到了最前端的 J\tt J 的位置,那么就可以确定一个最短的 JOI\tt JOI 串。

即我们可以暴力从前向后扫 J\tt J 的位置,然后依次找到 KKO\tt OI\tt I 即可。

可以对上面的算法进行优化,我们记录每个 J\tt JO\tt OI\tt I 的位置为 cjcoci,那么一段 J\tt J 的开始位置即为 cjjcj_j,结束位置为 cjj+k1cj_{j+k-1}O\tt OI\tt I 同理。

Core Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int n,k;
char s[N];
int cj[N],co[N],ci[N];
int totj,toto,toti;
int ans=INF;
int main()
{
n=read();k=read();
scanf("%s",s+1);
for(int i=1;i<=n;i++)
{
if(s[i]=='J') cj[++totj]=i;
if(s[i]=='O') co[++toto]=i;
if(s[i]=='I') ci[++toti]=i;
}
for(int i=1;i<=totj;i++)
{
if(i+k-1>totj) break;//后面不足 k 个 j,下面 o,i 同理
int ed=cj[i+k-1];//一段 j 的结束,下面 o,i 同理
int pos=1;
while(co[pos]<=ed && pos<=toto) pos++;//o 的起始位置,下面 i 同理
if(pos+k-1>toto) break;
ed=co[pos+k-1];
pos=1;
while(ci[pos]<=ed && pos<=toti) pos++;
if(pos+k-1>toti) break;
ed=ci[pos+k-1];
ans=min(ans,ed-cj[i]+1-3*k);//答案为枚举的区间长度与 3*k 的差
}
printf("%d\n",(ans==INF)?-1:ans);
return 0;
}

P6878 [JOI 2020 Final] JJOOII 2 题解
https://blog.makerlife.top/post/solution-P6878/
作者
Makerlife
发布于
2023年10月28日
许可协议