卡码网周赛第二十六期(23年阿里淘天笔试真题)

alt

T1:最小字典序

问题描述

时间限制:1.000S 空间限制:256MB

题目描述

小红有一个 01 字符串,她可以进行最多 k 次提作,每次操作可以交换相邻的两个字符,问可以得到的字典序最小的字符串是什么。
输入描述

第一行包含两个整数,n(1 < n < 10^5)和 k(1 < k < 10^9),表示字符串的长度和可以进行的操作次数。

接下来一行一个长度为 n 的 01 字符串。

输出描述

输出一个长度为 n 的字符串,表示字典序最小的字符串。

输入示例

5 2

01010

输出示例

00101

思路

这个题的n的范围是 1 0 5 10^5 105,k的范围是 1 0 9 10^9 109,所以暴力是不可能暴力的。

分析一下规律:

10101开始分析

k=101101

k=201011

k=300111

k>300111

总结起来就是说:从前往后扫,把前面的0尽可能前移

编程过程

Step1:记录前缀和(1的个数)

如 1010101010 ==> 1 1 2 2 3 3 4 4 5 5 我们只需要0对应的位置

前缀和记录的是1的个数,如pre[1] = 1指的是当前的0可以前移1位

pre[5] = 3:当前面两个0前移之后,变成00111 0 1010,此时pre[5]指的是当前的0可以前移3位

Step2:遍历字符串

Case1:`s[idx]=="1"`,idx += 1

Case2:`s[idx]=="0" and pre[idx] > 0`

    if k > pre[idx] : 把0移动至最前面
    else: 把0前移k-pre[idx]位

即:把K拆解成多个前缀之和,如s=1010101010 k=8 pre= [1 1 2 2 3 3 4 4 5 5]

8 = 1 + 2 + 3 + 2

代码

def fun(n,k,s):
    idx = 0
    # 前缀和
    pre =[0]
    for ss in s:
        if ss == "1":
            pre.append(pre[-1]+1)
        else:
            pre.append(pre[-1])
    # 处理超大K值
    _sum = 0
    for i,ss in enumerate(s):
        if ss == "0":
            _sum += pre[i+1]
    if k >= _sum:
        print("0"*s.count("0") + "1"*s.count("1"))
        return
    # 举个例子:1010101010 k=8
    # 记录一下前缀和:pre: 1 1 2 2 3 3 4 4 5 5  
    # 前缀和记录的是1的个数,如pre[1] = 1指的是当前的0可以前移1位
    # pre[5] = 3:当前面两个0前移之后,变成00111 0 1010,此时pre[5]指的是当前的0可以前移3位
     
    # 我们可以观察到0对应的前缀分别为1 2 3 4 5,那么8 = 1 + 2 + 3 + 2
    # 也就是说前三个0前移:0001111010,此时还剩下2次移动,00011 0 11 10
    while k > 0 and idx < n:
        while (idx < n and s[idx] == "1") or pre[idx+1]<=0: idx += 1
        if idx == n: break
        if k >= pre[idx+1]:
            s = "0"+s[:idx] + s[idx+1:]
            k -= pre[idx+1]
            pre[idx + 1] = -1
            if k == 0: break
        else:
            s = s[:idx-k] + "0" +s[idx-k:idx] + s[idx+1:]
            break
 
    print(s)
 
n,k = map(int,input().split(" "))
s = input()
fun(n,k,s)

T2:子序列个数

时间限制:1.000S 空间限制:256MB

题目描述

讨厌鬼有一个长度为 n (1 < n < 10^5)的数组,他想知道这个数组有多少个子序列是一个排列?

子序列的定义: 数组删除若干个元素(也可以不删)后得到的新数组。

排列的定义: 长度为 m 的数组,1 到 m 每个元素都出现过,且恰好出现 1 次。

输入描述

第一行输入一个整数 n。

第二行输入 n 个正整数,a1, a2, …, an。(1 <= ai <= 10^9)

输出描述

一行一个整数,表示有多少个子序列是一个排列。由于答案过大,请将答案对 10^ 9 + 7 取模后输出

输入示例

6

1 1 5 2 3 4

输出示例

10

提示信息

符合要求的子序列有:{1},{1},{1,2},{1,2},{1,2,3},{1,2,3},{1,2,3,4},{1,2,3,4},{1,5,2,3,4},{1,5,2,3,4}共 10 个

思路

n = 6,nums=1 1 5 2 3 4为例

cnt = {1:2,2:1,3:1,4:1,5:1}

其实就是计数

1 , 1-2, 1-3, 1-4, 1-5 的个数 ==>2, 2*1, 2*1*1, 2*1*1*2, 2*1*1*1*1 也就是 累乘和,所以又是一道前缀和

代码

from collections import Counter

n = int(input())
nums = list(map(int,input().split(" ")))
dic = Counter(nums)
nums = list(set(nums))
nums.sort()

idx = 1
ans = 0
if dic[1] == 0:
    print(0)
else:
    pre = [1]
    for num in nums:
        pre.append((pre[-1]*dic[num])%(10**9+7))
    # print(dic)
    # print(pre)
    while idx <= nums[-1]:
        if idx in dic:
            ans += pre[idx]
            ans = ans %(10**9+7)
            idx+=1
        else:
            break
    print(ans)


  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值