AcWing Round #14

A. 区间选数

题目链接


给定两个整数区间 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] [ l 2 , r 2 ] [l_2,r_2] [l2,r2]

请你找到两个整数 a a a b b b,要求:

l 1 ≤ a ≤ r 1 l_1\leq a\leq r_1 l1ar1
l 2 ≤ b ≤ r 2 l_2\leq b\leq r_2 l2br2
a ≠ b a\neq b a=b

输入格式

第一行包含整数 T T T,表示共有 T T T 组测试数据。

每组数据占一行,包含四个整数 l 1 , r 1 , l 2 , r 2 l_1,r_1,l_2,r_ 2 l1,r1,l2,r2

输出格式

每组数据输出一行结果,包含两个整数 a a a b b b

如果答案不唯一,输出任意合理方案均可。

保证一定有解。

数据范围

前三个测试点满足 1 ≤ T ≤ 10 1\leq T\leq10 1T10
所有测试点满足 1 ≤ T ≤ 500 1\leq T\leq 500 1T500 1 ≤ l 1 < r 1 ≤ 1 0 9 1\leq l_1 < r_1\leq 10^9 1l1<r1109 1 ≤ l 2 < r 2 ≤ 1 0 9 1\leq l_2 < r_2\leq 10^9 1l2<r2109

输入样例:
5
1 2 1 2
2 6 3 4
2 4 1 3
1 2 1 3
1 4 5 8
输出样例:
2 1
3 4
3 2
1 2
3 7
题目分析:

手速题,随便取数,只要二者不相等即可。

Code

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
    int T;
    cin >> T;
    int l1, l2, r1, r2;
    while (T -- )
    {
        bool flag = false;
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
        for (int i = l1; i <= r1; i ++ )
        {
            for (int j = l2; j <= r2; j ++ )
                if (i != j)
                {
                    printf("%d %d\n", i, j);
                    flag = true;
                    break;
                }
            if (flag) break;
        }
    }
    return 0;
}

B.食堂排队

题目链接


某班有 n n n 个学生,编号 1 ∼ n 1\sim n 1n

中午下课后,学生们陆续赶到食堂吃饭。

食堂只有一个打饭窗口,所以学生们要排队打饭。

i i i 个学生在第 l i l_i li 分钟开始排队(当然是排在队尾)。

如果同一分钟有多个学生同时开始排队,则编号较小的学生排在编号较大的学生之前。

当一名学生排在队伍的最前端时,即轮到该名学生打饭。

每个学生打饭需要花费一分钟时间。

打到饭的学生,立即离开队伍,没打到饭的学生,在队伍里继续等待。

每个学生的耐心都是有限的。

对于第 i i i 个学生,如果在第 r i r_i ri 分钟时,他仍在排队,且未轮到他打饭(仍有人排在他之前),那么他就会直接离开,放弃打饭。

对于每个学生,请你确定他是在第几分钟开始打饭(即排到队首)的?

输入格式

第一行包含整数 T T T,表示共有 T T T 组测试数据。

每组数据第一行包含整数 n n n

接下来 n n n 行,每行包含两个整数 l i , r i l_i,r_i li,ri,表示第 i i i个学生到达队伍的时间以及忍耐的极限时间。

保证同一组数据的 l i l_i li非递减的。

输出格式

每组数据输出一行结果,包含 n n n 个整数,其中第 i i i 个整数表示第 i i i 个学生开始打饭的时间,如果这个学生直接离开,放弃打饭,则输出 0 0 0

数据范围

前三个测试点满足 1 ≤ n ≤ 3 1\leq n\leq 3 1n3
所有测试点满足 1 ≤ T ≤ 1000 1\leq T\leq 1000 1T1000 1 ≤ n ≤ 1000 1\leq n\leq 1000 1n1000 1 ≤ l i ≤ r i ≤ 5000 1\leq l_i\leq r_i\leq 5000 1liri5000
同一测试点内所有 n n n 的和不超过 1000 1000 1000

输入样例:
2
2
1 3
1 4
3
1 5
1 1
2 3
输出样例:
1 2
1 0 2

题目分析:

由于题意已经明确,所有同学的插入队列时间都是递增的,因此我们只要按序模拟即可。

令最后一个人打饭结束的时间为last,我们需要判断后一个人的插入队列的时间l。如果l < last,则后一个人需要等待,若直到极限忍耐时间r都还没有打到饭,那么这个人直接溜了;如果l >= last,那么后一个人可以直接打饭,并且开始打饭的时间更新为开始打饭时间 + 1 即可。

Code

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main()
{
    int T;
    cin >> T;
    int n;
    while (T -- )
    {
        scanf("%d", &n);
        int last = 0;
        while (n -- )
        {
            int l, r;
            scanf("%d%d", &l, &r);
            // 如果开始打饭的时间大于极限忍耐时间,就走人
            if (max(last, l) > r) printf("0 ");
            else 
            {
                printf("%d ", max(last, l));
                last = max(last, l) + 1;
            }
        }
        puts("");
    }
    return 0;
}

C.寻找字符串

题目链接


给定一个由小写字母构成的字符串 s s s

请你找到一个满足如下所有要求的字符串 t t t

字符串 t t t 是字符串 s s s 的前缀。
字符串 t t t 是字符串 s s s 的后缀。
字符串 t t t 在字符串 s s s 的中间出现过。也就是作为一个既非前缀也非后缀的子串出现过。
字符串 t t t 的长度应尽可能长。

输入格式

第一行包含整数 T T T,表示共有 T T T 组测试数据。

每组数据占一行,包含一个字符串 s s s

输出格式

每组数据输出一行结果,如果 t t t 存在,则输出 t t t,否则输出 not exist

数据范围

前三个测试点满足 1 ≤ ∣ s ∣ ≤ 20 1\leq |s|\leq 20 1s20
所有测试点满足 1 ≤ T ≤ 10 1\leq T\leq 10 1T10 1 ≤ ∣ s ∣ ≤ 1 0 6 1\leq |s|\leq 10^6 1s106
同一测试点内所有输入字符串 s s s 的长度之和不超过 1 0 6 10^6 106

输入样例1:
2
fixprefixsuffix
abcdabc
输出样例1:
fix
not exist
前置知识:

KMP 算法,算法原理 & 分析见:AcWing 831. KMP字符串。

题目分析:

考虑第 1 , 2 , 4 1,2,4 1,2,4 要求,根据 KMP 算法原理,我们可以得知,当主串在s[i]和模式串的p[j + 1]处失配时,要让模式串后移尽量少的长度,也就是说我们需要充分利用模式串的信息,找到一个最大前后缀,然后令j = next[j],由于主串和模式串下标均从 1 1 1 开始,故在主串的匹配部分中,这个部分的最大前后缀长度就是next[j]。依次类推,次大前后缀长度为next[next[j]],次次大前后缀为……

挖掘出了条件 1 , 2 , 4 1,2,4 1,2,4 隐藏的性质,我们再来分析条件 3 3 3

对于主串s的任何一个前缀 s [ 1 ∼ i ] s[1\sim i] s[1i],其最大匹配后缀长度、次大匹配后缀长度…大小依次为:

\begin{align}
l_1 &= next[i] \\
l_2 &= next[l_1] \\
l_3 &= next[l_2] \\
&…
\end{align}

由上式可知,前i个字母,所有和后缀相等的长度,即每一个 l i l_i li,都是某一个 n e x t next next 的取值。

枚举每一个长度的前后缀,看它是否在中间出现过。

\begin{align}
l_1 &= next[n] \\
l_2 &= next[l_2] \\
l_3 &= next[l_3] \\
&…
\end{align}

无标题.png
l 1 l_1 l1 在中间出现过,则必然是某一个 1 ∼ k 1\sim k 1k 的前后缀,根据上面的分析,就必然是某一个 s [ l ∼ k ] s[l\sim k] s[lk] n e x t next next 值,即在 n e x t [ 1 ] ∼ n e x t [ n − 1 ] next[1]\sim next[n - 1] next[1]next[n1] 中,一定存在一个 n e x t next next 满足 n e x t = l 1 next = l_1 next=l1,表示在 s [ 1 ∼ k ] s[1\sim k] s[1k] 中,后 l 1 l_1 l1 个字母与前 l 1 l_1 l1 个字母匹配,即表示 l 1 l_1 l1 在中间出现过。

反过来,如果 n e x t = l 1 next = l_1 next=l1,也一定能推出 l 1 l_1 l1 在中间出现过。所以可以得到结论: l 1 l_1 l1 在中间出现过 ⇔ \Leftrightarrow n e x t [ 1 ] ∼ n e x t [ n − 1 ] next[1]\sim next[n - 1] next[1]next[n1] 中存在一个 n e x t = l 1 next = l_1 next=l1

Code

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e6 + 10;

int ne[N];
char s[N];
bool st[N];

int main()
{
    int T;
    cin >> T;
    int n = 0;
    while (T -- )
    {
        scanf("%s", s + 1);
        n = strlen(s + 1);
        // 初始化next数组
        for (int i = 2, j = 0; i <= n; i ++ ) 
        {
            while (j && s[i] != s[j + 1]) j = ne[j];
            if (s[i] == s[j + 1]) j ++ ;
            ne[i] = j;
        }
        
        memset(st, 0, sizeof st);
        // 所有在next[1] ~ next[n - 1]中出现过的,全部置为true 
        for (int i = 1; i < n; i ++ ) st[ne[i]] = true;
        int res = 0;
        for (int i = ne[n]; i; i = ne[i])
            if (st[i])
            {
                // i实际上是匹配到最后一个位置的下标
                res = i;
                break;
            }
        if (!res) puts("not exist");
        else
        {
            // 将最后一个位置的下标的后一位置为0,然后截断输出
            s[res + 1] = 0;
            printf("%s\n", s + 1);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值