字节跳动2019春招研发编程题

字节跳动2019春招研发编程题

  • 题目:万万没想到之聪明编辑
    • 题目描述:
    • 题目解析:
    • 实现代码:
      • 方法一:
      • 方法二:
      • 方法三:
  • 题目:万万没想到之抓捕孔连顺
    • 题目描述:
    • 题目解析:
    • 代码实现:
      • 方法二:
  • 题目:雀魂启动
    • 题目描述:
    • 题目解析:
    • 代码实现:
  • 题目:特征提取
    • 题目描述:
    • 题目解析:
  • 题目:毕业旅行问题
    • 题目描述:
    • 题目解析:
    • 代码实现:
  • 题目:找零
    • 题目解析:
    • 代码实现:
      • 方法一:
      • 方法二:
  • 题目:机器人跳跃问题
    • 题目描述:
    • 题目分析:
    • 代码实现:

题目:万万没想到之聪明编辑

题目描述:

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

  1. 三个同样的字母连在一起,一定是拼写错误,去掉一个的就好啦:比如 helllo -> hello
  2. 两对一样的字母(AABB型)连在一起,一定是拼写错误,去掉第二对的一个字母就好啦:比如 helloo -> hello
  3. 上面的规则优先“从左到右”匹配,即如果是AABBCC,虽然AABB和BBCC都是错误拼写,应该优先考虑修复AABB,结果为AABCC

我特喵是个天才!我在蓝翔学过挖掘机和程序设计,按照这个原理写了一个自动校对器,工作效率从此起飞。用不了多久,我就会出任CEO,当上董事长,迎娶白富美,走上人生巅峰,想想都有点小激动呢!

……

万万没想到,我被开除了,临走时老板对我说: “做人做事要兢兢业业、勤勤恳恳、本本分分,人要是行,干一行行一行。一行行行行行;要是不行,干一行不行一行,一行不行行行不行。” 我现在整个人红红火火恍恍惚惚的……

请听题:请实现大锤的自动校对程序

输入描述:

第一行包括一个数字N,表示本次用例包括多少个待校验的字符串。

后面跟随N行,每行为一个待校验的字符串。

输出描述:

N行,每行包括一个被修复后的字符串。

输入例子1:

2
helloo
wooooooow

输出例子1:

hello
woow

题目解析:

这道题的描述比较清楚,逻辑简单,只要基于上面三个规则对字符串进行处理就行,但是在测试用例的时候,如果用暴力枚举的方法,时间复杂度会肯定太大,测试通不过,所以必须采用其他方法

方法一:暴力枚举,遍历字符串,先处理三个及以上连在一起的,处理后的字符串在从头开始遍历,每四个为一个组,判断是否符合第二条规定,符合则删除四个中的最后一个元素。

判断条件:

i = i-1 and i-2 = i-3

方法二:队列,遍历一次字符串,然后从头开始入队,符合两个条件不予入队

​ 条件一:当元素和队尾及队尾第二个元素相同时,构成三个元素,不予入队

		e == stack[-1] and stack[-1] == stack[-2]

​ 条件二:当元素等于队尾第一个元素及队尾第二个元素和第三个元素相等时,符合上面的条件,不予入队

		e == stack[-1] and stack[-2] == stack[-3]

​ 最后将队转换为字符串输出

方法三:正则表达式,最简单的方法,正则是用于对文本的处理,可以很方便的处理字符串

​ 步骤一:处理三个及以上

​ Python正则表达式:

		re.sub(r"(.)\1+","\\1\\1",str)
		re.sub(r"(.)\1(.)\2","\\1\\1\\2",res)

实现代码:

方法一:

n = int(input())
lt = []
for i in range(n):
    lt.append(input())
for strs in lt:
    ls = list(strs)
    sign = 0
    count = 2
    while count < len(ls):
        if count < len(ls) and ls[count - 2] == ls[count] and ls[count - 1] == ls[count]:
            del ls[count]
        else:
            count += 1
    for i in range(3,len(ls)):
       if i < len(ls) and ls[i] == ls[i-1] and ls[i-2] == ls[i-3]:
           del ls[i]
    strs = ""
    print(strs.join(ls))

可以实现,但是超时

方法二:

n = int(input())
while n > 0:
    s = input()
    res = []
    for e in s:
        if len(res) < 2:
            res.append(e)
            continue
        if len(res) >= 2:
            if e == res[-1] and e == res[-2]:
                continue
        if len(res) >= 3:
            if e == res[-1] and res[-2] == res[-3]:
                continue
        res.append(e)
    print("".join(res))
    n -= 1

方法三:

import re
n = int(input())
ls = []
for i in range(n):
    ls.append(input())
for str in ls:
    res = re.sub(r"(.)\1+","\\1\\1",str)
    rs = re.sub(r"(.)\1(.)\2","\\1\\1\\2",res)
    print(rs)

题目:万万没想到之抓捕孔连顺

题目描述:

  1. 我们在字节跳动大街的N个建筑中选定3个埋伏地点。
  2. 为了相互照应,我们决定相距最远的两名特工间的距离不超过D。

我特喵是个天才! 经过精密的计算,我们从X种可行的埋伏方案中选择了一种。这个方案万无一失,颤抖吧,孔连顺!

……

万万没想到,计划还是失败了,孔连顺化妆成小龙女,混在cosplay的队伍中逃出了字节跳动大街。只怪他的伪装太成功了,就是杨过本人来了也发现不了的!

请听题:给定N(可选作为埋伏点的建筑物数)、D(相距最远的两名特工间的距离的最大值)以及可选建筑的坐标,计算在这次行动中,大锤的小队有多少种埋伏选择。

注意:

  1. 两个特工不能埋伏在同一地点
  2. 三个特工是等价的:即同样的位置组合(A, B, C) 只算一种埋伏方法,不能因“特工之间互换位置”而重复使用

输入描述:

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

第二行包含N个建筑物的的位置,每个位置用一个整数(取值区间为[0, 1000000])表示,从小到大排列(将字节跳动大街看做一条数轴)

输出描述:

一个数字,表示不同埋伏方案的数量。结果可能溢出,请对 99997867 取模

示例1

输入

4 3
1 2 3 4

输出

4

说明

可选方案 (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)

示例2

输入

5 19
1 10 20 30 50

输出

1

说明

可选方案 (1, 10, 20)

题目解析:

方法一:排列组合,列出所有情况然后,判断,是否符合,这种枚举的方法很显然会超时

方法二:因为是从小到大排列的,所以判断区间内的最大值和最小值是否可以满足条件,如何满足使用排列组合的公式直接计算结果

代码实现:

方法二:

n,d = map(int,input().split())
ls = list(map(int,input().split()))
count = 0
def fun(n):
    return (n-1)*n // 2
for i in range(2,n):
    j = 0
    while i -j >= 2 and ls[i] - ls[j] > d :
        j += 1
    count = (count + fun(i-j)%99997867) % 99997867

print(count%99997867)

题目:雀魂启动

题目描述:

小包最近迷上了一款叫做雀魂的麻将游戏,但是这个游戏规则太复杂,小包玩了几个月了还是输多赢少。

于是生气的小包根据游戏简化了一下规则发明了一种新的麻将,只留下一种花色,并且去除了一些特殊和牌方式(例如七对子等),具体的规则如下:

  1. ​ 总共有36张牌,每张牌是1~9。每个数字4张牌。
  2. ​ 你手里有其中的14张牌,如果这14张牌满足如下条件,即算作和牌
  • ​ 14张牌中有2张相同数字的牌,称为雀头。

  • ​ 除去上述2张牌,剩下12张牌可以组成4个顺子或刻子。顺子的意思是递增的连续3个数字牌(例如234,567等),刻子的意思是相同数字的3个数字牌(例如111,777)

    例如:

    1 1 1 2 2 2 6 6 6 7 7 7 9 9 可以组成1,2,6,7的4个刻子和9的雀头,可以和牌

    1 1 1 1 2 2 3 3 5 6 7 7 8 9 用1做雀头,组123,123,567,789的四个顺子,可以和牌

    1 1 1 2 2 2 3 3 3 5 6 7 7 9 无论用1 2 3 7哪个做雀头,都无法组成和牌的条件。

    现在,小包从36张牌中抽取了13张牌,他想知道在剩下的23张牌中,再取一张牌,取到哪几种数字牌可以和牌。

输入描述:

输入只有一行,包含13个数字,用空格分隔,每个数字在1~9之间,数据保证同种数字最多出现4次。

输出描述:

输出同样是一行,包含1个或以上的数字。代表他再取到哪些牌可以和牌。若满足条件的有多种牌,请按从小到大的顺序输出。若没有满足条件的牌,请输出一个数字0

示例1

输入

1 1 1 2 2 2 5 5 5 6 6 6 9

输出

9

说明

可以组成1,2,6,7的4个刻子和9的雀头

示例2

输入

1 1 1 1 2 2 3 3 5 6 7 8 9

输出

4 7

说明

用1做雀头,组123,123,567或456,789的四个顺子

示例3

输入

1 1 1 2 2 2 3 3 3 5 7 7 9

输出

0

说明

来任何牌都无法和牌

备注:

请不要根据自己的常识为题目增加未提及的条件

对于20%的数据,保证和牌时一定是4个刻子+雀头的形式

题目解析:

依次将0-9个数字加入到序列中,然后看是否可以构成胡牌,判断构成胡牌,先判断是否是顺子

顺子的规律是一共只有5种牌,所以先用字典统计序列各个元素的个数,然后判断字典的大小。

相比于顺子,刻子的判断是比较难的,首先去除雀头,也就是判断字典中,值大于或等于2的键,然后再判断省下的序列是否可以组成刻子。

刻子判断,先取第一个数,然后判断这个数+1和+2是否在序列中,如果在从序列中删除这个三个数,不在结束循环,不构成刻子

代码实现:

import copy
#判断是否胡牌
def fun(ls):
    dic = {}
    for item in ls:
        dic[item] = ls.count(item)
    sign = False
    if len(dic) == 5:
        for key,value in dic.items():
            if value > 3 :
                break
            if value == 1 or value == 2 :
                sign = True
    else:
        for key,value in dic.items():
            if value >=5:
                break
            if value >= 2:
                rs = copy.deepcopy(ls)
                rs.remove(key)
                rs.remove(key)
                sign = win(rs)
                if sign :
                    break
    return sign
#判断是否是刻字
def win(ls):
    while ls != []:
        num = ls[0]
        del ls[0]
        if num + 1 in ls and num + 2 in ls:
            ls.remove(num+1)
            ls.remove(num+2)
        else:
            return False
    return True

ls = list(map(int,input().split()))
lt = []
#一次将1-9个数加入到牌中
for i in range(1,10):
    rs = copy.deepcopy(ls)
    rs.append(i)
    rs.sort()
    if fun(rs):
        lt.append(i)
if lt == [] :
    print(0)
else:
    for i in lt:
        print(i,end=" ")





题目:特征提取

题目描述:

小明是一名算法工程师,同时也是一名铲屎官。某天,他突发奇想,想从猫咪的视频里挖掘一些猫咪的运动信息。为了提取运动信息,他需要从视频的每一帧提取“猫咪特征”。一个猫咪特征是一个两维的vector。如果x_1=x_2 and y_1=y_2,那么这俩是同一个特征。

​ 因此,如果喵咪特征连续一致,可以认为喵咪在运动。也就是说,如果特征在持续帧里出现,那么它将构成特征运动。比如,特征在第2/3/4/7/8帧出现,那么该特征将形成两个特征运动2-3-4 和7-8。

现在,给定每一帧的特征,特征的数量可能不一样。小明期望能找到最长的特征运动。

输入描述:

第一行包含一个正整数N,代表测试用例的个数。

每个测试用例的第一行包含一个正整数M,代表视频的帧数。

接下来的M行,每行代表一帧。其中,第一个数字是该帧的特征个数,接下来的数字是在特征的取值;比如样例输入第三行里,2代表该帧有两个猫咪特征,<1,1>和<2,2>
所有用例的输入特征总数和<100000

N满足1≤N≤100000,M满足1≤M≤10000,一帧的特征个数满足 ≤ 10000。
特征取值均为非负整数。

输出描述:

对每一个测试用例,输出特征运动的长度作为一行

示例1

输入

1
8
2 1 1 2 2
2 1 1 1 4
2 1 1 2 2
2 2 2 1 4
0
0
1 1 1
1 1 1

输出

3

说明

特征<1,1>在连续的帧中连续出现3次,相比其他特征连续出现的次数大,所以输出3

备注:

如没有长度大于2的特征运动,返回1

题目解析:

首先使用字典存储特征(使用字符串或者取一个很大的数),遍历这个帧数,记录上一次的特征字典,计算这一次的特征字典,如果上一次出现了,则将上一次的特征数加入到这一次中,然后记录最大的连续出现的特征数

代码实现:

n = int(input())
res = 0
while n > 0 :
    m = int(input())
    #记录上一个的特征
    dic = {}
    for j in range(m):
        ls = list(map(int, input().split()))
        k = ls[0]
        index = 0
        #记录当前的特征
        dic_d = {}
        for i in range(k):
            #字典的键
            index = ls[ 2 * i + 1 ] * 1000000 + ls[2 * i + 2]
            if index in dic:
                #如果再上一次的字典中,就将上一次的键值加一
                dic_d[index] = dic[index] + 1
                #判断最大值
                res = max(res,dic_d[index])
            else:
                dic_d[index] = 1
        #将新的字典覆盖上一个的字典,作为下一个的新字典
        dic = dic_d
    n -= 1
print(res)


题目:毕业旅行问题

题目描述:

小明目前在做一份毕业旅行的规划。打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,希望能够通过合理的路线安排尽可能的省一些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。

输入描述:

城市个数n(1

输出描述:

最小车费花销 s

示例1

输入

4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0

输出

13

说明

共 4 个城市,城市 1 和城市 1 的车费为0,城市 1 和城市 2 之间的车费为 2,城市 1 和城市 3 之间的车费为 6,城市 1 和城市 4 之间的车费为 5,依次类推。假设任意两个城市之间均有单程票可购买,且票价在1000元以内,无需考虑极端情况

题目解析:

动态规划问题,重点是建立状态转移方程,也就是数组的计算

解析:https://www.nowcoder.com/test/question/done?tid=36476257&qid=362294#summary

代码实现:

n = int(input())
m = []
for i in range(n):
    m.append(list(map(int, input().split())))

V = 1 << (n-1)  #从左至右每一位二进制代表第i个城市是否被访问 如1000代表,第一个城市被访问,而其他城市没有
dp = [[float("inf")] * V for i in range(n)] # dp[i][j]:从节点i只经过集合j所有点再回到0点所需要的最小开销

for i in range(n):
    dp[i][0] = m[i][0]

for j in range(1,V):
    for i in range(n):
        for k in range(1,n):  #能不能先到k城市
            if (j >> (k-1) & 1) == 1: #可以途径k
                dp[i][j] = min(dp[i][j], m[i][k] + dp[k][j ^ (1 << (k-1))])

#从0出发,经过所有点,再回到0的费用
print(dp[0][(1 << (n-1)) - 1])


题目:找零

Z国的货币系统包含面值1元、4元、16元、64元共计4种硬币,以及面值1024元的纸币。现在小Y使用1024元的纸币购买了一件价值为N(0

输入描述:

一行,包含一个数N。

输出描述:

一行,包含一个数,表示最少收到的硬币数。

示例1

输入

200

输出

17

说明

花200,需要找零824块,找12个64元硬币,3个16元硬币,2个4元硬币即可。

备注:

对于100%的数据,N(0

题目解析:

经典的01背包问题,实现方法有二种

方法一:贪心算法,这道题贪心可以求出最优解

​ 从最大的面额开始找,不够最大面额再换小一点的,因为有一元的,所以肯定可以找开

方法二:动态规划,01背包问题的解法

​ 状态转移方程:

​ dp[i] = min(dp[i],dp[i-m]+1)

对于01背包问题,贪心有时候可能求不出最优解,看情况分析

代码实现:

方法一:

n = 1024 - int(input())
#动态规划
dp = [1024] * (n+1)
dp[0] = 0
money = [1,4,16,64]
for i in range(1,n+1):
    for m in money:
        if i - m >=0:
            dp[i] = min(dp[i],dp[i-m]+1)
print(dp[-1])

方法二:

n = 1024 - int(input())
money = [64,16,4,1]
count = 0
for i in money:
    count += n // i
    n = n%i
print(count)

题目:机器人跳跃问题

题目描述:

机器人正在玩一个古老的基于DOS的游戏。游戏中有N+1座建筑——从0到N编号,从左到右排列。编号为0的建筑高度为0个单位,编号为i的建筑的高度为H(i)个单位。

起初, 机器人在编号为0的建筑处。每一步,它跳到下一个(右边)建筑。假设机器人在第k个建筑,且它现在的能量值是E, 下一步它将跳到第个k+1建筑。它将会得到或者失去正比于与H(k+1)与E之差的能量。如果 H(k+1) > E 那么机器人就失去 H(k+1) - E 的能量值,否则它将得到 E - H(k+1) 的能量值。

游戏目标是到达第个N建筑,在这个过程中,能量值不能为负数个单位。现在的问题是机器人以多少能量值开始游戏,才可以保证成功完成游戏?

输入描述:

第一行输入,表示一共有 N 组数据.

第二个是 N 个空格分隔的整数,H1, H2, H3, ..., Hn 代表建筑物的高度

输出描述:

输出一个单独的数表示完成游戏所需的最少单位的初始能量

示例1

输入

5
3 4 3 2 4

输出

4

示例2

输入

3
4 4 4

输出

4

示例3

输入

3
1 6 4

输出

3

备注:

数据约束:
1 <= N <= 10^5
1 <= H(i) <= 10^5

题目分析:

倒推法,从0开始,计算初始值

代码实现:

import math
n = int(input())
h = [0]+list(map(int, input().split()))
E = [0]*(n+1)
for i in range(n-1,-1,-1):
    E[i] = (E[i+1]+h[i+1])*0.5
print(math.ceil(E[0]))

你可能感兴趣的:(算法,python,算法)