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