招新赛补题7-7原神生日会和7-8漏字文

7-7.原神生日会

有a个空(男旅行者)和b个荧(女旅行者)在会场外排队参加原神生日会。不幸的是,会场里只有N个座位,而且已经有若干个派蒙(可以是0个)已经坐在会场里面了。

  • 每个男旅行者都希望坐在他边上的是女旅行者或者派蒙。
  • 每个女旅行者都希望坐在她边上的是男旅行者或者派蒙。

换句话说,男旅行者不希望和其他男旅行者坐一起,女旅行者不希望和其他女旅行者坐一起。

如果会场内没有符合旅行者心意的位置,那么旅行者会选择不参加原神生日会。

作为活动组织者的你,希望能参加生日会的旅行者尽可能的多,现在给出会场内每个派蒙的位置,请你求出最多有几个旅行者会来参加生日会。

输入格式:

输入共两行。
第一行有三数,分别为N,a,b(1≤n≤2∗105,0≤a,b≤2∗105,a+b>0)。表示会场内有N个位子,会场外有a个男旅行者,有b个女旅行者。

第二行是一个由字符"."和字符"P"组成的字符串。

"."表示空位,"P"表示这个位置被派蒙占了。

输出格式:

输出一个整数,表示最多可以有几个旅行者参加生日会。

输入样例:

在这里给出一组输入。例如:

11 3 10
.P....PP.P.

输出样例:

在这里给出相应的输出。例如:

7

样例解释

我们用P来表示派蒙,A来表示男旅行者,B来表示女旅行者,最终会场内的座位情况是这样的:BPABABPPAPB

        这题在当时咋一看感觉挺难的,但是仔细一想才发现原来没那么难,(现在的题目都成阅读理解题了)首先要找到每组连续的空位以及空位的个数,先是考虑偶数个的情况。比如现在有4个位置,那么只能是 男-女-男-女女-男-女-男,可以这么说,无论怎么分配,都是男女参半,不可能有其他情况。

        主要是奇数个的情况,假设现在有3个连续的空位,那么我们可以是 男-女-男 或者 女-男-女,一共有两种不同的分法。我们可以这么认为,先把空位-1进行平均分配,然后剩余的一个空位就可以自由分配了。所以我们要记录下所有的能自由分配的空位数,我们不妨先分配给男生,再逐一分配给女生。

        由于题目中的男生和女生是有固定数量的,所以我们应先考虑所有情况中理论上分配的个数,再根据实际情况确定出实际分配的个数。最后逐一比较找出最佳答案。

#include <iostream>
using namespace std;
inline int min(int a, int b)
{
    return a<b? a:b;
}
int main()
{
    int N, a, b;
    cin >> N >> a >> b;
    char *str = new char[N+1]{0};
    cin >> str;
    int lian = 0;           //一次连续座位的个数
    int na = 0, nb = 0, left = 0;
    //na是目前分配的男生个数,nb是女生,left是能自由分配的位置
    for(int i = 0; i < N+1; i++)
    {
        if(str[i] == '.')
        {
            lian ++;        //记录连续座位的个数
        }
        else        //如果遇到'P'则开始处理
        {
            //先把位置尽量分给男生
            na += (lian+1)/2;
            nb += lian-(lian+1)/2;
            if(lian%2)
            {
                left ++;    //记录可以自由分配的位置的个数
            }
            lian = 0;       //处理完一轮后连续位置个数置零
        }
    }
    int result = min(a, na) + min(b, nb);
    for(int i = 1; i <= left; i++)
    {
        na --;      //现在把空闲的位置分给女生
        nb ++;      
        if(result < min(a, na) + min(b, nb))
            result = min(a, na) + min(b, nb);
    }
    cout << result << endl;
    delete[] str;
    return 0;
}

7-8漏字文

有意避免使用某个或某几个特定字母的写成的文章称漏字文(英语lipogram,希腊语lipagrammatos,意为“失踪的字母”),漏字文可能在写作或文字游戏中出现。公元前5世纪希腊诗人里斐奥多鲁斯(Tryphiodorus)写的24卷史诗中,每一卷都省略希腊字母中的一个不同字母。

现在给你一段仅由空格和小写字母组成的字符串,请你找出其中最长的一段连续的漏字文,并输出漏字文的长度(即该段文字中字母的个数)。

输入格式:

输入共两行。
第一行一个数N(1≤N≤105),表示有N个由小写字母组成的单词。
第二行是一个字符串,有N个长度不超过10的单词,每个单词间用一个空格隔开。

输出格式:

一个整数,表示漏字文的长度。

输入样例:

在这里给出一组输入。例如:

9
the quick brown fox jumps over the lazy dog

输出样例:

在这里给出相应的输出。例如:

32

样例解释

“the quick brown fox jumps over the lazy dog”包含了所有的字母。
其中最长的漏字文是“the quick brown fox jumps over the lazy”,长度是32(即里面有32个字母)。这段文字没有字母d和g,符合漏字文的条件。

        这题相对前七题而言比较有难度。首先要注意的一点是题目所求的最长的漏字文并不是一定要从第一个单词开始的(我开始受到测试数据的影响我以为所求的漏字文要从第一个单词开始结果就默默地失败了),如果要从第一个单词开始查找,再从第二个单词开始查找......这样子就会太费劲,而且会运行超时。

        所以现在的问题就是既要遍历所有的情况,又要减少冗余的查找,比如说如果从1到n的单词构成了第一个全字文,那么我们就没必要继续往后面查找了,就直接把“句子的头”指向第二个单词,那么我们也没必要继续2-3,2-4这样逐个查找,因为我们已经有一个长度为n-1的最长漏字文了,我们不移动“句子的尾”,而是检查移动“头”移动一次之后是否为漏字文,如果不是漏字文,同理我们没必要移动“尾巴”了,而是继续移动“头”直至为漏字文而如果“头”移动一次后就构成了漏字文,那么我们可以继续移动“尾”直到找到下一个最长的漏字文后,再移动“头”,周而复始,找到最佳结果。

        于是我们提出双指针策略,一个头指针,一个尾指针,初始时都指向单词数组的头部

        先移动尾指针直至找到了第一个最长的漏字文为止(尾指针碰上全字文)

        接下来尾指针不移动,移动头指针,直到找到了漏字文

        继续移动尾指针,让漏字文变长,直至找到了下一个最长的漏字文(碰上全字文)

        继续循环直至找到所有情况,用一个变量来记录最长的漏字文个数

        接下来就是上代码了,这里多说一下,我们用一个数组来记录每个字母出现的个数,查找漏字文的话每次都要循环遍历这个数组,比较繁琐,因此我们用一个变量来记录字母的种类数,每添加或者删除一个单词更新这个变量,只需要判断是不是26就可以了。我们可以把增删单词封装为函数,这样的话我们的实现就会方便很多。为了方便变量在函数中的传递,我这里使用全局变量,当然我们也可以用引用来传参,这样会好很多。

#include <iostream>
using namespace std;
//用一个cnt来计数字母的个数要比循环遍历sum好
int sum[26];        //记录每个字母出现的次数
int cnt = 0;        //字母种类的个数和结果
//增加一个单词
void add(string str)
{
    for(int i = 0; i < str.length(); i++)
    {
        if((sum[str[i]-'a']++) == 0)
        {
            cnt ++;
        }
    }
}
//减少一个单词
void sub(string str)
{
    for(int i = 0; i < str.length(); i++)
    {
        if(--(sum[str[i]-'a']) == 0)
        {
            cnt --;
        }
    }
}

int max(int a, int b)
{
    return a>b? a:b;
}
int main()
{
    int N;
    cin >> N;
    string *str = new string[N];
    for(int i = 0; i < N; i++)
        cin >> str[i];
    
    int l = 0, r = 0;
    int ans = 0, now = 0;//遍历中找极大值往往采取这种方法
    for(r = 0; r < N; r++)
    {
        add(str[r]);
        now += str[r].length();
        while(cnt == 26)
        {
            sub(str[l]);
            now -= str[l].length();
            l++;        //别忘记了这里的l要后移
        }
        ans = max(ans, now);
    }
    cout << ans << endl;
    return 0;
}

最后,这是本人第一次发博客,没太多经验,希望大家看到能给些合理的建议,谢谢大家。 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值