【例题】lanqiao2928 分糖果

在这里插入图片描述
样例输入

6 2
caabdc

样例输出

abccd

一个最优分配方案是一个同学拿到 abccd ,一个同学拿到a 。

题目解读

n:糖果的种类有n种,每种糖果用小写字母表示

x:x个同学

开心程度s:糖果组成的字符串s[i]的字典序

目标:开心程度相差尽可能小,每个同学至少有一个糖果

输出:字典序最小的s数组里最大的max(s[i])

解题思路

字典序即字母在字典中出现的顺序,故【b开头的字符串】>【a开头的字符串】,参考字典序这篇文章

为了实现【开心程度相差尽可能小,每个同学至少有一个糖果】的目标,要求每个同学拿到的糖果的首字母尽可能一样或者顺延,起始字母相同再比较后几位字母。

字典序比较大小

【b开头的字符串】>【a开头的字符串】

比较差距只要是看两个字符串从左到右出现不同的那个字母之间的差距是多少个字典序。

比如abc与ab的差距 > abbc与a的差距:

abc>abbc>a:

  • abc等价于abcaaaaaa…,ab等价于abaaaaaa…,二者出现差异的字母是第三个字母c和a
  • abbc等价于abbcaaaa…,a等价于aaaaaaa…,二者出现差异的字母是第二个字母b和a

由于ac之间的字典序差异肯定比ab之间的字典序差异大,所以abc与ab的差距 > abbc与a的差距。

代码实现

ans = []
n,x = map(int,input().split())
s = input()
# 构造一个数组,用来存储每个字母出现的次数
cnt = [0] * 26
# ord() 函数返回对应的 ASCII 数值,或者 Unicode 数值
for i in s:
    cnt[ord(i) - ord('a')] += 1
ans0 = []
# 按字母序从小到大遍历每个字母
for i in range(26):
    # s里面没有这个字母
    if not cnt[i]:
        continue
    if cnt[i] < x:
        k = 1
        break
    elif cnt[i] == n:
        k = 2
        break
    elif cnt[i] == x and cnt.count(0) == 24:
        k = 3
        break
    # 其他情况,比如cnt[i] > n,...,k=4
    else:
        k = 4
        break
# x个同学不能都分到这个字母,k=1
if k == 1:
    for i in range(26):
        x -= cnt[i]
        # cnt[i]>=x,说明每个同学至少分一颗
        if x <= 0:
            # 得到这个字母
            # 由于字母序越后,字母的ASCII值越大,为了输出最大值,从前往后不断迭代
            ans0 = [chr(i + ord('a'))]
            break
# x个同学都能分到这个字母,k=2
elif k == 2:
    # cnt.index(x)输出的是糖数量刚好为x的第一个品种的索引
    # n // x 表示对x个同学,每人平分的糖的数量,加上 min(1, n % x),为了输出最大的,加上余数,并且保证每个人至少分到一颗
    ans0 = [chr(cnt.index(x) + ord('a'))] * (n // x + min(1, n % x))
# x个同学都能分到这个字母,且s里只有两个不同字母,k=3
elif k == 3:
    f = 0
    ans0 = []
    for i in range(26):
        # i不在那两个字母之中
        if not cnt[i]:
            continue
        # f==0,说明还没有找到第一个字母
        # 当运行到这一步,说明已经找到第一个字母了
        if not f:
            # 标记找到第一个字母,并把这个最小的字母分配给第一个学生
            f = 1
            ans0.append(chr(i + ord('a')))
        # 如果 f==1,表示我们已经找到了第一个字母,现在添加的是第二个字母
        # 将这个字母(数量为 x)分配给除了第一个学生之外的所有学生,因此每个学生得到 (n // x + min(1, n % x) - 1) 个这种字母的糖果。
        else:
            ans0.append(chr(i + ord('a')) * (n // x + min(1, n % x) - 1))
else:
    for i in range(26):
        # 寻找第一个非零数量的糖果类型,并从该类型的糖果中减去 x - 1,这意味着至少为其他学生留出 x - 1 个这种类型的糖果。
        if cnt[i]:
            cnt[i] -= x - 1
            break
    ans0 = []
    for i in range(26):
        ans0.append(chr(i + ord('a')) * cnt[i])
ans.append("".join(ans0))
print(''.join(map(str,ans)))
n,x = map(int,input().split())
S = input()
S = sorted(S) # 将糖果S按从小价值的到大价值的排序

# 1、确定起始字母不同,故那个不同的字母就是最大的
# 于s里面第一种糖果不能分给x个同学每人一颗
if S[x-1] != S[0]: 
# 分配给最后一个小朋友的糖果价值肯定是字母序最大的,输出即可
    print(S[x-1])
# 2、起始字母相同,后面的字母都相同
else: 
	ans=[]
	# 经过排序的s的最后一个字母和除起始字母外的第一个字母相等,说明剩余糖果都是一个品种
    if S[x] == S[-1]:
    	# 输出起始字母
        ans.append(S[x-1])
        # 按照每个小朋友平均分配的方式分配糖果,第一个同学会拿到最多的,所以输出第一个模拟输出第一个同学累积分配到的糖果
        # 步长为小朋友的个数
        for i in range(x,n,x): 
            ans.append(S[i])
        print(''.join(ans))
# 3、起始字母相同,后面的糖果不相同
    else:
        for i in S[x-1:]: # 假如俩同学,字符串为aabccd,则为 a;abccd
            print(i,end='')

参考分糖果-胡口翼的代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值