代码练习1~

第一题

我叫王大锤,是一家出版社的编辑。我负责校对投稿来的英文稿件,这份工作非常烦人,因为每天都要去修正无数的拼写错误。但是,优秀的人总能在平凡的工作中发现真理。我发现一个发现拼写错误的捷径:

1. 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello
2. 两对一样的字母(AABB型)连在一起,一定是拼写错误,去掉第二对的一个字母就好啦:比如 helloo -> hello
3. 上面的规则优先“从左到右”匹配,即如果是AABBCC,虽然AABB和BBCC都是错误拼写,应该优先考虑修复AABB,结果为AABCC
num = input("please enter the num: ")     # data为str类型
output_str = []
for xxx in range(int(num)):
    str = input()
    i = 0
    while i < len(str):
        a = len(str)
        if i < (len(str) - 2):
            while str[i] == str[i+1] and str[i+1] == str[i+2]:
                str = str.replace(str[i + 2], "", 1)
                if i >= (len(str) - 2):
                    break
        if i < (len(str) - 3):
            while str[i]==str[i+1] and str[i+2] == str[i+3]:
                str = str.replace(str[i+3],"", 1)
                if i >= (len(str) - 3):
                    break
        i = i+1
    output_str.append(str)

for yyy in range(int(num)):
    print(output_str[yyy])

第二题

请听题:给定 N(可选作为埋伏点的建筑物数)、 D(相距最远的两名特工间的距离的最大值)以及可选建筑的坐标,计算在这次行动中,大锤的小队有多少种埋伏选择。
注意:
1. 两个特工不能埋伏在同一地点
2. 三个特工是等价的:即同样的位置组合( A , B , C ) 只算一种埋伏方法,不能因“特工之间互换位置”而重复使用

输入描述:
第一行包含空格分隔的两个数字 N和D(1 ≤ N ≤ 1000000; 1 ≤ D ≤ 1000000)

第二行包含N个建筑物的的位置,每个位置用一个整数(取值区间为[0, 1000000])表示,从小到大排列(将字节跳动大街看做一条数轴)
输出描述:
一个数字,表示不同埋伏方案的数量。结果可能溢出,请对 99997867 取模
num_maxDistance = input("输入建筑个数以及最长距离,用空格隔开:")
distances = input("建筑之间的距离(从小到大,空格分离):")
num = int(num_maxDistance.split()[0])
maxDistance = int(num_maxDistance.split()[1])
distances_list = results = list(map(int, distances.split()))
j = 0
count = 0
mod = 99997867
for i in range(num):
    # 倒序搜索,固定最后一个建筑,向前寻找前两个建筑
    while i >= 2 and (distances_list[i]-distances_list[j]) > maxDistance:
        j = j+1
    front = i - j
    count = front * (front-1) / 2 + count
print(count%mod)

第三题

有三只球队,每只球队编号分别为球队1,球队2,球队3,这三只球队一共需要进行 n 场比赛。现在已经踢完了k场比赛,每场比赛不能打平,踢赢一场比赛得一分,输了不得分不减分。已知球队1和球队2的比分相差d1分,球队2和球队3的比分相差d2分,每场比赛可以任意选择两只队伍进行。求如果打完最后的 (n-k) 场比赛,有没有可能三只球队的分数打平。

输入描述:
第一行包含一个数字 t (1 <= t <= 10)
接下来的t行每行包括四个数字 n, k, d1, d2(1 <= n <= 10^12; 0 <= k <= n, 0 <= d1, d2 <= k)

输出描述:
每行的比分数据,最终三只球队若能够打平,则输出“yes”,否则输出“no”
编程思路:参考绅士猫
题目中只说队伍之间相差的分数,并没有说哪支队伍得分多,哪支队伍得分少。所以,本题应该分4种情况讨论。
假设球队1得分为m (m >= 0) ,至少需要need 场比赛才能持平。
我们分四种情况讨论时,应注意到判断属于哪种情况的条件是判定3 * m的值符合哪一种情况。

当 球队1< 球队2,球队2<球队3 时,得分情况:
        球队1:m
        球队2:m + d1
        球队3:m + d1 + d2 。此时有3 * m = k - d1 - d1 - d2    need = d1 + d2 + d2(此时球队3得分
        最多,所以球队1还需要赢d1 + d2场,球队2还需要赢d2场)
 
当 球队1< 球队2,球队2>球队3 时,得分情况:
        球队1:m
        球队2:m + d1
        球队3:m + d1 - d2 。此时有3 * m = k - d1 - d1 + d2    need = d1 + d2 (此时球队2得分最多,所以球队1还需要赢d1场,球队3还需要赢d2场)
 
当 球队1> 球队2,球队2>球队3 时,得分情况:
        球队1:m
        球队2:m - d1
        球队3:m - d1 - d2 。此时有3 * m = k + d1 + d1 + d2    need = d1 + d1 + d2 (此时球队1得分最多,所以球队2还需要赢d1场,球队3还需要赢d1 + d2场)
 
当 球队1> 球队2,球队2<球队3 时,得分情况:
        球队1:m
        球队2:m - d1
        球队3:m - d1 + d2 。
        此时有3 * m = k + d1 + d1 - d2  。这时不能确定哪个球队得分最多,还要分情况:
                    当 d1  >= d2 时,球队1得分最多need = d1 + d1 - d2
                    当 d1  <   d2 时,球队3得分最多need = d2 - d1 + d2
 
但是写代码时,不能用if   else if  else if....这种结构,因为同一组数据可能满足两种以上的情况,
所以要把这几种情况都判断一遍,如果都不能输出yes,那么最后输出no
t = int(input())
output = []
for i in range(t):
    [n, k, d1, d2] = [int(x) for x in input().split(' ')]

    # 球队1< 球队2,球队2<球队3
    tmp = k - d1 - d1 - d2
    if (tmp >= 0) and (tmp % 3 == 0):
        remain = (n - k) - (d1 + d2 + d2)
        if (remain >= 0) and (remain % 3 == 0):
            output.append("yes")
            continue # 继续循环

    # 球队1< 球队2,球队2>球队3
    tmp = k - d1 - d1 + d2
    if (tmp >= 0) and (tmp % 3 == 0):
        remain = (n - k) - (d1 + d2)
        if (remain >= 0) and (remain % 3 == 0):
            output.append("yes")
            continue

    # 球队1> 球队2,球队2>球队3
    tmp = k + d1 + d1 + d2
    if (tmp >= 0) and (tmp % 3 == 0):
        remain = (n - k) - (d1 + d1 + d2)
        if (remain >= 0) and (remain % 3 == 0):
            output.append("yes")
            continue

    #球队1> 球队2,球队2<球队3
    tmp = k + d1 + d1 - d2
    if (tmp >= 0) and (tmp % 3 == 0):
        if d1 >= d2:
            remain = (n - k) - (d1 + d1 - d2)
        else:
            remain = (n - k) - (d2 - d1 + d2)
        if (remain >= 0) and (remain % 3 == 0):
            output.append("yes")
            continue

    output.append("no")
    
for j in range(t):
    print(output[j])

第四题

有一个仅包含’a’和’b’两种字符的字符串s,长度为n,每次操作可以把一个字符做一次转换(把一个’a’设置为’b’,或者把一个’b’置成’a’);但是操作的次数有上限m,问在有限的操作数范围内,能够得到最大连续的相同字符的子串的长度是多少。
输入描述:
第一行两个整数 n , m (1<=m<=n<=50000),第二行为长度为n且只包含’a’和’b’的字符串s。
输出描述:
输出在操作次数不超过 m 的情况下,能够得到的 最大连续 全’a’子串或全’b’子串的长度。
该字符串非 a 即 b 也就是说在区间 l~r之间把所有字符变为 a 所需的步骤数是 该区间内所有字符b改为字符a。反之亦然.
 用数组 count[i] 表示 字符串在位置区间 0~i 中包含的 a 的个数,则 区间 l~r 的 a 的个数为 count[r] - count[l - 1]
 b 的个数用 a 的个数算出 即 区间 l~r 的 b 的个数为 r + 1 - count[r] - (l + 1 - 1 - count[l - 1]) = r + 1 - l - count[r] + count[l - 1]
 在区间 l~r 的 a 和 b 的个数已知的情况下
 若 区间长度step内的 a 的个数 <= m 则 可以通过 m 个步骤 产生 长度为step的连续字符串 b
 若 区间长度step内的 b 的个数 <= m 则 可以通过 m 个步骤 产生 长度为step的连续字符串 a
 归纳为 :若 区间长度step内的字符 b 或字符 a 的个数 <= m 则 可以通过 m 个步骤 产生 长度为step的字符串(不管是全a还是全b)
 这样 就可以直接计算出一个字符串长度(区间长度step)是否可行,因此不需要进行递推,可以直接进行二分搜索,得到最大长度。
 检查一个长度step是否可行的时间复杂度为O(n),二分搜索的时间复杂度为O(log n)。
 因此,该方法总的时间复杂度为 O(n*log n)

附二分搜索:
假设在[begin,end)范围内搜索某个元素 v,mid == (begin + end)/ 2
①、如果v < m,去[begin , mid)范围内二分搜索
②、如果v > m,去[mid + 1, end)范围内二分搜索
③、如果v == m ,直接返回 mid
import math
def func(step, m, a_len_dict):
    i = 0
    while (i + step)<len(str):
        # 区间b的个数
        if m >= (step + 1 - (a_len_dict[i + step] - a_len_dict[i - 1])):
            return True
        # 区间a的个数
        if m >= (a_len_dict[i + step] - a_len_dict[i - 1]):
            return True
        i = i + 1
    return False

if __name__ == '__main__':
    [n, m] = [int(x) for x in input().split(' ')]
    str = input('只包含“a”和“b”的字符串:')
    a_len_dict = {}
    a_len_dict[-1] = 0
    count = 0
    # 记录每一个位置上a的数量
    for i in range(n):
        if str[i] == 'a':
            count = count + 1
        a_len_dict[i] = count
    # 二分搜索最大区间
    begin = 0
    end = n - 1
    while(begin < end): # 判定条件是<,而不是<=
        mid = math.floor((end + begin) / 2)
        if func(mid, m, a_len_dict):
            begin = mid + 1
        else:
            end = mid   # 向下取整时,end=mid;
    
    # 跳出循环的时候,begin==end,但是不知道从0到begin区间长度是否能组成连续相同字符串,因此需要判断
    if func(begin, m, a_len_dict):
        print(begin + 1)
    else:
        print(begin)

第五题

输入描述:
第1行为n代表用户的个数
第2行为n个整数,第i个代表用户标号为i的用户对某类文章的喜好度
第3行为一个正整数q代表查询的组数
第4行到第(3+q)行,每行包含3个整数l,r,k代表一组查询,即标号为l<=i<=r的用户中对这类文章喜好值为k的用户的个数。 数据范围n <= 300000,q<=300000 k是整型
输出描述:
一共q行,每行一个整数代表喜好值为k的用户的个数
时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 256M,其他语言512M
输入例子:
5
1 2 3 3 5
3
1 2 1
2 4 5
3 5 3
输出例子:
1
0
2
例子说明:
样例解释:
有5个用户,喜好值为分别为1、2、3、3、5,
第一组询问对于标号[1,2]的用户喜好值为1的用户的个数是1
第二组询问对于标号[2,4]的用户喜好值为5的用户的个数是0
第三组询问对于标号[3,5]的用户喜好值为3的用户的个数是2 
这道问题的关键在于时间限制。(因为空间有512M是明显足够的。)简单粗暴的for循环+list时间就超过6秒了。可以考虑for循环+dict。
附用字典的原因:
列表、字典的时间复杂度比较:字典查找速度快,无论dict有10个元素还是10万个元素,查找速度都一样。而list的查找速度随着元素增加而逐渐下降。不过dict的查找速度快不是没有代价的,dict的缺点是占用内存大,还会浪费很多内容,list正好相反,占用内存小,但是查找速度慢。
n = int(input())
li = [int(a) for a in input().split()]
d = {}
for i,v in enumerate(li):
    if v not in d:
        d[v] = [i + 1]
    else:
        d[v].append(i+1)
q = int(input())
for _ in range(q):
    l, r, k = map(int, input().split())
    if k not in d.keys():
        print(0)
    else:
        a = 0
        for j in d[k]:
            if j>=l and j<=r:
                a += 1
        print(a)

第六题

手串
作为一个手串艺人,有金主向你订购了一条包含n个杂色串珠的手串——每个串珠要么无色,要么涂了若干种颜色。为了使手串的色彩看起来不那么单调,金主要求,手串上的任意一种颜色(不包含无色),在任意连续的m个串珠里至多出现一次(注意这里手串是一个环形)。手串上的颜色一共有c种。现在按顺时针序告诉你n个串珠的手串上,每个串珠用所包含的颜色分别有哪些。请你判断该手串上有多少种颜色不符合要求。即询问有多少种颜色在任意连续m个串珠中出现了至少两次。
输入描述:
第一行输入n,m,c三个数,用空格隔开。(1 <= n <= 10000, 1 <= m <= 1000, 1 <= c <= 50) 
接下来n行每行的第一个数num_i(0 <= num_i <= c)表示第i颗珠子有多少种颜色。
接下来依次读入num_i个数字,每个数字x表示第i颗柱子上包含第x种颜色(1 <= x <= c)
输出描述:
一个非负整数,表示该手链上有多少种颜色不符需求。
输入例子:
5 2 3
3 1 2 3
0
2 2 3
1 2
1 3
输出例子:
2
例子说明:
第一种颜色出现在第1颗串珠,与规则无冲突。
第二种颜色分别出现在第 1,3,4颗串珠,第3颗与第4颗串珠相邻,所以不合要求。
第三种颜色分别出现在第1,3,5颗串珠,第5颗串珠的下一个是第1颗,所以不合要求。
总计有2种颜色的分布是有问题的。 
这里第2颗串珠是透明的。
本题关键点:
(1)使用字典dict来做,以颜色做关键字,将出现该颜色的珠子存入列表作为键的值。
(2)是否满足问题的判断条件是:针对每一个键的值,先正序排序后,判断相邻的两个值是否满足间隔<m !!(因为如果相邻的两个值<m的话,肯定不符合要求。反之,如果相邻的两个值>=m,那么其他两两之间肯定是>=m,符合要求。)
(3)因为手环是一个环,所以针对每一个键的值,要判断值是否<m,如果<m,那么最后的(m-1)个珠子加上m会回到这些值上来,因此list需要加上(i+n),如果i<m的话
n, m, c = map(int, input().split())
dict_1 = {}
for i in range(n):
    a = list(map(int, input().split()))[1:]
    for item in a:
        if item not in dict_1:
            dict_1[item] = [i]
        else:
            dict_1[item].append(i)
        if (i<m):
            dict_1[item].append(i + n)
count = 0
for j in dict_1:
    b = dict_1[j]
    b.sort()
    for x in range(1, len(b)):
        if ((b[x] - b[x-1]) < m):
            count = count + 1
            break
print(count)

第七题

以下函数使用二分查找搜索一个增序的数组,当有多个元素值与目标元素相等时,返回最后一个元素的下标,目标元素不存在时返回-1。请指出程序代码中错误或不符最佳实践的地方(问题不止一处,请尽量找出所有你认为有问题的地方)

int BinarySearchMax(const std::vector<int>& data, int target)
{
   int left = 0;
   int right = data.size();
   while (left < right) {
       int mid = (left + right) / 2;
       if (data[mid] <= target)
           left = mid + 1;
       else
           right = mid - 1;
   }

   if (data[right] == target)
       return right;
   return -1;
}
答案:
(1)int right = data.size();  --> int right = data.size() - 1;
(2)int mid = (left + right) / 2;   -->   int mid = Math.floor((left + right) / 2);
(3)right = mid - 1;   -->   right = mid;
(4)此外,判断条件应该增加一个:
if (data[right - 1] == target)
    return right - 1;
附:完整的二分搜索算法(当有多个元素值与目标元素相等时,返回最后一个元素的下标)
def BinarySearchMax(data, target):
    l = 0
    r = len(data) - 1
    while(l<r):
        mid = math.floor((l + r) / 2)
        if (data[mid] <= target):
            l = mid + 1
        else:
            r = mid
    # l==r, 但是不知道l或者r是不是处于最后一个位置,如果是的话,返回r即可;若不是,需要判断r-1个是不是符合要求,如符合,则返回r-1
    if (data[r]==target):
        return r
    if (data[r-1]==target):
        return r-1
    return -1
附:完整的二分搜索算法(当有多个元素值与目标元素相等时,返回第一个元素的下标)
def BinarySearchMax(data, target):
    l = 0
    r = len(data) - 1
    while(l<r):
        mid = math.floor((l + r) / 2)
        if (data[mid] < target):    # 关键在这里,去掉等号即可
            l = mid + 1
        else:
            r = mid
    # l==r, 但是不知道l或者r是不是处于最后一个位置,如果是的话,返回r即可;若不是,需要判断r-1个是不是符合要求,如符合,则返回r-1
    if (data[r]==target):
        return r
    if (data[r-1]==target):
        return r-1
    return -1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值