Acwing 5468. 最有价值字符串【挖掘性质+分类讨论】

原题链接:https://www.acwing.com/problem/content/5471/

题目描述:

A,B,C 三人在玩一个有关字符串的游戏。

给定三人每人一个由大小写字母构成的字符串。

三人的字符串的长度相同。

规定,一个字符串的价值等于该字符串中出现次数最多的子串的出现次数。

例如,aaaaaa 的价值为 6,因为出现次数最多的子串 a 一共出现了 6 次;abab 的价值为 2,因为出现次数最多的子串 ab 一共出现了 2 次。

游戏开始后,每人都需要对自己的字符串进行恰好 n 次修改,每次修改需要将字符串中的某个字符修改为另一个不同的字符,例如将 aaab 修改为 acab

所有修改完毕后,最有价值字符串的拥有者将获得游戏胜利。

请你计算,如果所有人都采取最优策略,那么谁将最终获胜。

输入输出描述:

输入格式

第一行包含整数 n。

第二行,包含一个由大小写字母构成的字符串,表示给 A 的字符串。

第三行,包含一个由大小写字母构成的字符串,表示给 B 的字符串。

第四行,包含一个由大小写字母构成的字符串,表示给 C 的字符串。

保证这三个人获得的字符串的长度均相同。

输出格式

共一行,A 获胜则输出 A,B 获胜则输出 B,C 获胜则输出 C,不止一人获胜则输出 D

数据范围

前 6 个测试点满足 0≤n≤20,每个输入字符串的长度范围 [1,20]。
所有测试点满足 0≤n≤10^9,每个输入字符串的长度范围 [1,10^5]。

输入样例1:
3
abcdd
efgcd
hijgk
输出样例1:
A
输入样例2:
7
abcdefbcgfhi
igbccjbkchle
gkmnlcjnboce
输出样例2:
B
输入样例3:
1
abcabc
cbabac
ababca
输出样例3:
C
输入样例4:
15
abcdefghi
jkdlbmnop
jqrstduve
输出样例4:
D

解题思路:

这个题目只要观察一下挖掘出一个性质,那么这个题目就很简单了,但是如果没挖掘出来可能就会认为比较难了,赛时我很快就发现了这个性质,但是有一种情况一直没考虑到导致虽然赛时做出来了,但是wa了好几发,下面我举个例子描述一下我是怎么发现这个性质的。

例子1:ababab

对于上述例子1,我们可以发现子串ab出现了三次,同时这个字符串中的出现次数最多的子串出现的次数也是3,我们观察可以发现,ab出现了三次,自然ab中的a和b都出现了三次,也就是说子串a和子串b也都出现了三次, 我们可以发现对于某个子串出现了k次,那么这个子串中的每个字符都至少出现k次,当子串中有重复字符时,那么某个字符的出现次数会大于k,也就是说我们只需要考虑长度为1的子串即可,因为长度等于1的子串出现的次数肯定大于等于长度大于1的子串出现的次数。

通过上述分析,我们现在只需要考虑长度为1的子串即可,对于三个字符串中的任意一个字符串的修改操作,肯定是把当前这个字符串的所有字符都修改为同一种字符,那么我们肯定首先拿到出现次数最多的那个字符ch,把其他不等这个ch的字符都修改为字符ch.

设我们当前某个字符串出现次数最多的字符出现的次数为k,此时题目给出的修改次数为n,当前字符串长度为len,下面我们来分类讨论一下。

(1)k<len

当k+n<=len,出现次数最多的字符出现的次数就是k+n

当k+n>len,我们首先让所有字符相同,多余的操作次数我们可以放在某个位置上消耗完,例如acaaa,当n==3,我们可以先把c变为不等于a的其他字符,例如字符b,再变为字符c,然后把这个字符再变为a,就是先把多余的操作次数消耗掉,在变成需要的字符即可,此时出现次数最多的字符出现次数最终就可以是len。

(2)k==len

当n==1,最开始所有字符相同,我们必须要操作一次,我们只能某个位置的字符修改掉,那么出现次数最多的字符出现了len-1次。

当n!=1时,我们可以向上面(1)中的k+n>len这种情况一样,先消耗多余的操作,那么出现次数最多的字符出现次数仍然是len次。

时间复杂度:O(len),len表示字符串的长度。

空间复杂度:O(len),len表示字符串的长度。

cpp代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>

using namespace std;

const int N=1e5+10;

int n,m;
int main()
{
    cin>>n;
    string s1,s2,s3;
    cin>>s1>>s2>>s3;
    unordered_map<char,int>cnt1,cnt2,cnt3;
    for(auto& t:s1)cnt1[t]++;
    for(auto& t:s2)cnt2[t]++;
    for(auto& t:s3)cnt3[t]++;
    
    int max1=0,max2=0,max3=0;
    for(auto& t:cnt1)max1=max(max1,t.second);
    for(auto& t:cnt2)max2=max(max2,t.second);
    for(auto& t:cnt3)max3=max(max3,t.second);
    
    bool ok1=false,ok2=false,ok3=false;
    int len=s1.size();
    if(max1==len){
        if(n==1)max1=len-1,ok1=true;
    }
    if(max2==len){
        if(n==1)max2=len-1,ok2=true;
    }
    if(max3==len){
        if(n==1)max3=len-1,ok3=true;
    }
    
    if(!ok1)max1=min(max1+n,len);
    if(!ok2)max2=min(max2+n,len);
    if(!ok3)max3=min(max3+n,len);
    
    if(max1>max(max2,max3))cout<<"A"<<endl;
    else if(max2>max(max1,max3))cout<<"B"<<endl;
    else if(max3>max(max1,max2))cout<<"C"<<endl;
    else cout<<"D"<<endl;
    return 0;
}
  • 18
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值