华为
ICT光产品部二面
整数拆分
牛客原题:整数拆分
一个整数总可以拆分为2的幂的和,例如:
7 = 1+2+4
7 = 1+2+2+2
7 = 1+1+1+4
7 = 1+1+1+2+2
7 = 1+1+1+1+1+2
7 = 1+1+1+1+1+1+1
总共有六种不同的拆分方式。
再比如:4可以拆分成:
4 = 4
4 = 1 + 1 + 1 + 1
4 = 2 + 2
4 = 1+1+2
用f(n)表示n的不同拆分的种数,例如f(7)=6. 要求编写程序,读入n(不超过1000000),输出f(n)%1000000000。
分析:
分为奇数和偶数进行讨论
当N为奇数时,它里面一定包含一个奇数成分1,因为其他成分全是偶数。所以它只能通过它的前一个数N-1通过+1得到
当N为偶数时,它里面可以是有奇数成分1,也可以没有。所以它可以通过它的前一个数N-1通过+1得到,也可以通过N//2数的成分乘以2得到,这样里面的成分就全是偶数
综上动归方程就为:
d p [ i ] = { d p [ i − 1 ] , i % 2 ! = 0 d p [ i − 1 ] + d p [ i / / 2 ] , i % 2 = = 0 dp[i] = \begin{cases} dp[i-1], & i\%2 != 0 \\ dp[i-1] + dp[i//2], & i\%2 == 0 \end{cases} dp[i]={
dp[i−1],dp[i−1]+dp[i//2],i%2!=0i%2==0
Python源码之动态规划:
number = int(input())
dp = [0] * (number + 1)
dp[1] = 1
for i in range(2, number + 1):
if i&1 == 1:
dp[i] = dp[i-1] % 1000000000
else:
dp[i] = (dp[i-1] + dp[i>>1]) % 1000000000
print(dp[-1])
Python源码之回溯:
def demo(n):
res = []
path = []
def backtracking(val, startIndex):
if val == 0:
res.append(path[:])
return
if val < 0:
return
i = startIndex
while 2 ** i <= val:
val -= 2 ** i
path.append(2 ** i)
backtracking(val, i)
path.pop()
val += 2 ** i
i += 1
backtracking(n, 0)
return res
res = demo(7)
print(len(res))
print(res)
打印结果:
6
[[1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 2],
[1, 1, 1, 2, 2],
[1, 1, 1, 4],
[1, 2, 2, 2],
[1, 2, 4]]
升级版:
一个整数被拆分成小于等于它的非负数数之和的拆分方式,即不限制其拆分成分为2的幂
分析:
整数拆分问题的四种解法
问题定义:
dp[i][j]:表示数字i可以被最大为j的整数拆分的种数
动态迁移方程,分为三种情况:
1)当i=j的时候,此时的拆分种数dp[i][j]只会比dp[i][j-1]的种数多一种情况,即i可以用j来表示。所以dp[i][j]=dp[i][j-1]+1
2)当i<j的时候,此时的拆分种数dp[i][j]等于dp[i][i],因为对于大于i的j来说他们是无法去拆分整数i的,也就相当于大于i的部分j是没有作用的。所以dp[i][j]=dp[i][i]
3)当i>j的时候,此时的拆分种数dp[i][j]有两种情况组成,第一,拆分里面存在数字j,这种情况的种数为dp[i-j][j];第二,拆分里面不存在数字j,这种情况的种数为dp[i][j-1]
d p [ i ] [ j ] = { d p [ i ] [ j − 1 ] + 1 , i = = j d p [ i ] [ i ] , i < j d p [ i − j ] [ j ] + d p [ i ] [ j − 1 ] , i > j dp[i][j] = \begin{cases} dp[i][j-1] + 1, & i==j \\ dp[i][i], & i<j \\ dp[i-j][j] + dp[i][j-1], & i>j \end{cases} dp[i][j]=⎩
⎨
⎧dp[i][j−1]+1,dp[i][i],dp[i−j][j]+dp[i][j−1],i==ji<ji>j
初始化:
当i=1或者j=1时,情况只有1种
Python源码之动态规划:
def dfs(n):
dp = [[0] * (n+1) for _ in range(n+1)]
for i in range(1, n+1): dp[i][1] = 1
for i in range(1, n+1): dp[1][i] = 1
for i in range(2, n+1):
for j in range(2, n+1):
if i == j: dp[i][j] = dp[i][j-1] + 1
elif i < j: dp[i][j] = dp[i][i]
else: dp[i][j] = dp[i-j][j] + dp[i][j-1]
return dp[-1][-1]
Python源码之回溯:
def demo(n):
res = []
path = []
def backtracking(val, startIndex):
if val == 0:
res.append(path[:])
return
if val < 0:
return
i = startIndex
while i <= val:
val -= i
path.append(i)
backtracking(val, i)
path.pop()
val += i
i += 1
backtracking(n, 1)
return res
res = demo(4)
print(len(res))
print(res)
打印结果:
5
[[1, 1, 1, 1],
[1, 1, 2],
[1, 3],
[2, 2],
[4]]
终端BG-AI与智慧全场景一面
字符串转换
牛客面经:华为-AI工程师-消费者BG-面经
面经整理计划——第六弹
分析:
贪心思想
1)针对target中的每个字符就去block中去一一匹配
2)如果匹配成功,就target和block都右移一位,继续匹配
3)如果匹配不成功,则res+1,同时block从头开始匹配
4)如果找完整个block都没有匹配成功,则返回-1,说明无法从block中得到target
Python源码:
def demo(target, block):
res = 0
i = 0
while i < len(target):
j = 0
flag = True
while j < len(block):
if i < len(target) and target[i] == block[j]:
i += 1
flag = False
j += 1
if flag: return -1
res += 1
return res
target = 'abcd' # 'aaa', 'abcd'
block = 'bcad' # 'ad', 'bcad'
print(demo(target, block))
无连续1的最小字符串
给定一个只包含0和1的字符串,判断其中有无连续的1。若有,则输出比该串大的无连续1的最小值串。若无,则不做操作。
例:给定 ‘11011’ ,则输出 ‘100000’ ;给定 ‘10011’ ,则输出 ‘10100’ 。
分析:
还是贪心的思想
1)从左往右寻找,找到最开始连续的两个1,如果没有则直接返回原字符串
2)在连续的位置上,将前一个符号(0)变为1,后面的符号全变为0
3)变换之后再继续检查时候有连续的1,如果没有就直接返回
Python源码:
def demo(strInput):
start = check(strInput)
while start != -1:
if start == 0:
strInput = '1' + '0'*len(strInput)
else:
strInput = strInput[:start-1] + '1' + '0'*(len(strInput)-start)
start = check(strInput)
return strInput
def check(strInput):
for i in range(len(strInput)-1):
if strInput[i] == strInput[i+1] and strInput[i] == '1':
return i
return -1
print(demo('10011'))
集合子集
给定一个集合,输出该集合的所有子集,并给出所编写算法的时间复杂度。
例:给定集合 [1, 2, 3],输出 [[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]。
LeetCode原题:剑指 Offer II 079. 所有子集
分析:
回溯思想,分为两种情况,即当前元素是否放入子集中
Python源码:
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = []
path = []
def backtracking(cur):
if cur == len(nums):
res.append(path[:])
return
# 当前元素放入path中
path.append(nums[cur])
backtracking(cur + 1)
path.pop()
# 当前元素不放入path中
backtracking(cur + 1)
backtracking(0)
return res
大数乘法
以字符串的形式读入两个数字,编写一个函数计算它们的乘积,以字符串形式返回。
牛客原题:NC10 大数乘法
分析:
优秀题解:
【算法】大数乘法问题及其高效算法
Karatsuba大数乘法算法
python大数相乘
Python源码:
class Solution:
def solve(self , s: str, t: str) -> str:
# write code here
if s == '0' or t == '0': return '0'
res = [0] * (len(s) + len(t))
sList = list(s)
tList = list(t)
sList.reverse()
tList.reverse()
# 将t里面的每个数字与s相乘,并保存在对应为res位数上
for i in range(len(sList)):
for j in range(len(tList)):
res[i+j] += int(sList[i]) * int(tList[j])
# 将res位数上的数字进行取余和求商运算,余数放在当前位置上,商进行进位
for i in range(len(res)):
temp = res[i]
res[i] = str(temp % 10)
if i < len(res)-1: res[i+1] = res[i+1] + temp // 10
# 返回结果,注意移除res前面的0
res.reverse()
resStr = ''.join(res)
for i in range(len(resStr)):
if resStr[i] != '0': break
resStr = resStr[i:]
return resStr
最长回文子串
LeetCode原题:5. 最长回文子串
终端BG-AI与智慧全场景二面
滑动窗口
LeetCode原题:209. 长度最小的子数组
百度
百度自动驾驶一面
重复字符的速记方式
有一种重复字符的速记方式
(1)重复的部分会被以“(重复内容)<重复次数>”形式记录,并且可能存在嵌套缩写关系
(2)不重复的部分按原样记录;
先给定一个字符串str,请实现一个函数恢复字符串速记前的内容,str仅由小写字母、数字、()、<>组成。
例如:
str=“abc(d)<2>”,速记前字符串为:“abcdd”
str=“a(b(c)<2>d)<3>e”,速记前字符串为:“abccdbccdbccde”
分析:
第一想法是简单的,那就是要使用栈,那怎么来记录这些特殊字符和字母让我再面试的犯难了,一时间没想出来,后面静静一下就明白,步骤如下:
1)根据下标i循环将str中的字符压入栈stack中
2)判断当前字符是不是符号")“,如果是就说明当前遇到重复字符了,这里需要注意一个点,如果是")“符号,则执行步骤3)、4)、5),执行完后循环下标i需要跳过”<>“里面的内容,如果不是”)"符号,则i+1,进入下一轮的循环
3)读取符号”)“后面”<>“里面的内容,即获取重复的次数
4)读取符号”)“前面到最近的一个”(“符号,实现方法是,while循环从栈stack中pop出元素,将弹出来的元素放在一个新的临时栈temp_stack中,知道遇到符号”(“,则停止循环,此时temp_stack里面就存放了当前的重复内容
5)根据重复的次数,再依次地将temp_stack的元素放回栈stack中,注意放回去的时候还是要从temp_stack的栈顶开始遍历
6)直到字符串str中所有的元素遍历结束,stack里面就存放了速记前的内容了饿,直接返回”".join(stack),就完成整个函数
Python源码
def demo(strInput):
stack = []
i = 0
while i < len(strInput):
if strInput[i] == ')':
# 读取 <> 里面的重复次数
j = i + 2 # 跳过 < 符号
times = ""
while strInput[j] != '>':
times += strInput[j]
j += 1
times = int(times)
# 读取 ) 符号到最近 ( 符号里面的重复内容
str_val = stack.pop()
temp_stack = []
while str_val != '(':
temp_stack.append(str_val)
str_val = stack.pop()
# 根据重复次数依次将temp_stack里面的元素再放回stack中
for i in range(times):
for v in range(len(temp_stack)-1, -1, -1):
stack.append(temp_stack[v])
# 下标 i 跳位,跳过 <> 符号以及里面的重复次数
i = j + 1
else:
stack.append(strInput[i])
i += 1
return "".join(stack)
有效的括号
因为上面那道题面试的时候没有做出来,面试官比较和谐,又给了一道简单题,这题是LeetCode的一道原题,还好做过,一口气就写出来了
LeetCode:20. 有效的括号
代码随想录:20. 有效的括号
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
1)左括号必须用相同类型的右括号闭合。
2)左括号必须以正确的顺序闭合。
例子:
输入:s = “(]”,输出:false
输入:s = “()[]{}”,输出:true
输入:s = “([)]”,输出:false
分析:
1)快速判断,如果str的长度不是一个偶数,说明肯定不匹配,可直接返回False
2)依次将str放入stack栈中
3)判断当前字符是不是")“、”]“、”}"中的一个,如果是,就将stack里面的元素pop一个出来,判断弹出来的元素是不是跟当前字符是不是匹配的,如果不是直接返回False
4)最后返回的时候只需判断stack是否为空,如果是的则返回True
Python源码
def demo(strInput):
# 快速判断
if len(strInput) % 2 != 0: return False
stack = []
str_pairs = {
')': '(', ']': '[', '}': '{'}
for v in strInput:
if v in str_pairs:
# 细节问题:应该要先判断stack是否为空
# 不然当第一个元素就为')'、']'或'}'时,程序会报错
# 面试时候这点没有考虑到,直接判断了pop出来的元素
if len(stack) == 0 or stack[-1] != str_pairs[v]:
return False
else:
stack.pop()
else:
stack.append(v)
# 细节问题:我面试的时候写的是
# if len(stack) == 0: return True
# else: return False
# 这里被面试官提醒了,说这个返回太臃肿了,可以不可以简化一下
# 结果我没说出来
# 面试官直接说了:
# return len(stack) == 0
# return not stack 可能是最简洁的了,又学习了
return not stack
字节
字节智能创作一面
有序数组的平方
直接出了一道简单题,但因为前面被PUA得太厉害了,脑袋一片空白,这道刷过的题没有AC掉,心里非常的难受
LeetCode:977. 有序数组的平方
代码随想录:977. 有序数组的平方
与LeetCode原题有一点细微差别就是要求返回的是不重复的元素
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
例子:
输入:nums = [-5,-4,-1,0,1,3,5,5],输出:[0,1,9,16,25]
输入:nums = [-4,-1,0,3,10],输出:[0,1,9,16,100]
分析
1)定义首尾双指针,即left和right指针,分别指向输入数组的第一个元素和最后一个元素
2)判断left和right指向的元素的平方值,将较大的那个指针的平方值放入result的末端,注意一定是末端,所以得使用insert,面试的时候我直接使用了append,直接导致我这道题失败,因为脑袋已经空白,这么简单问题也没有发现,然后移动指针,如果是left就右移,如果是right就左移
Python源码
def demo(nums):
result = []
left, right = 0, len(nums)-1
pre = -1 # 记录添加到result里面的最新元素,用来避免重复元素的添加
while left <= right: # 一定要有等于,不然会漏掉一个元素
left_square = nums[left] * nums[left]
right_square = nums[right] * nums[right]
if left_square >= right_square: # 取出那个较大的平方值
if left_square != pre:
# 注意一定是insert,而不是append!!!
# 插入到数组的末端
result.insert(0, left_square)
pre = left_square
left += 1
else:
if right_square != pre:
result.insert(0, right_square)
pre = right_square
right -= 1
return result
TnS内容安全一面
手机屏幕解锁方式
输入:m=2,n=2,s=2。m,n表示矩阵的大小,即m行n列,s表示解锁的步数,即划过几个点
输出:‘1->2’, ‘1->3’, ‘1->4’, ‘2->1’, ‘2->3’, ‘2->4’, ‘3->1’, ‘3->2’, ‘3->4’, ‘4->1’, ‘4->2’, ‘4->3’
如果m=2,n=2的话,矩阵就为:
[[1, 2],
[3, 4]]
如果m=3,n=3的话,矩阵就为:
[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
分析:
本题思路还是不难,直接就能想到用回溯
难点就是代码能力考察
相似的题
LeetCode:79. 单词搜索
Python源码
def demo(m, n, s):
res = []
path = []
# 生成矩阵
# m=2, n=2: [[1,2], [3,4]]
# m=3,n=3: [[1,2,3], [4,5,6], [7,8,9]]
board = [[0] * n for _ in range(m)]
start = 1
for i in range(m):
for j in range(n):
board[i][j] = start
start += 1
def backtracking(board, i, j):
# 如果路径长度等于s,说明该路径已经完成,直接返回True
if len(path) == s:
res.append('->'.join(path))
return True
# 如果回溯的下标不满足矩阵的范围,就返回False
if not (0<=i and i<m and 0<=j and j<n): return False
# 用-1表示这个位置已经经过,就返回False
if board[i][j] == -1: return False
# 在path中添加当前元素
path.append(str(board[i][j]))
temp = board[i][j]
# 将当前元素置为-1,表示当前元素已经经过
board[i][j] = -1
flag = False
for si in range(-1, 2):
for sj in range(-1, 2):
if si == 0 and sj == 0: continue
if backtracking(board, i+si, j+sj):
flag = True
break
if flag: break
# 弹出当前元素
path.pop()
# 重新归位当前元素
board[i][j] = temp
# 遍历所有元素,定义路径的起点
for i in range(m):
for j in range(n):
backtracking(board, i, j)
return res
print(demo(2, 2, 2))
TnS内容安全二面
查找问题
LeetCode原题:33. 搜索旋转排序数组
81. 搜索旋转排序数组 II
分析:
核心思想:
将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。
此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环
1)将中间值与当前左边的值进行比较,如果中间值大于等于当前左边的值,说明中间值的左边是有序的。然后再判断目标值是否在左边这个有序数组中,如果在,缩小右边范围;如果不在,则缩小左边范围
2)如果中间值小于当前左边的值,说明中间值的右边是有序的。然后再判断目标值是否在右边这个有序数组中,如果在,缩小左边范围;如果不在,则缩小右边范围
具体见二分法之旋转数组
Python源码:
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums)-1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
if nums[mid] >= nums[left]:
if nums[left] <= target <= nums[mid]:
right = mid - 1
else:
left = mid + 1
else:
if nums[mid] <= target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1
TnS内容安全三面
滑动窗口
给定一个字符串,找到其中长度最长的没有重复字符的字串
例如:输入 ‘abbbcdefff’,输出 ‘bcdef’
Python源码:
def check(alphaDict):
for key, val in alphaDict.items():
if val > 1: return False
return True
def demo(strInput):
maxLen = 0
res = ''
left = right = 0
alphaDict = dict()
while right < len(strInput):
if strInput[right] not in alphaDict: alphaDict[strInput[right]] = 1
else: alphaDict[strInput[right]] += 1
while not check(alphaDict):
alphaDict[strInput[left]] -= 1
left += 1
tmpLen = right - left + 1
if tmpLen > maxLen:
maxLen = tmpLen
res = strInput[left:right+1]
right += 1
return res
print(demo('abbbcdefff'))
蔚来
自动驾驶二面
字符串转换
同华为BG的一面题
快手
计算机视觉一面
计算IoU
美团
北斗-无人配送车一面
旋转链表
LeetCode原题:61. 旋转链表
滑动窗口最大值
LeetCode原题:239. 滑动窗口最大值