ARTICLE · 1065519
华为机考非AI方向9月23日笔试题与解析
写在前面
本次给大家带来2026年9月23日华为非AI方向笔试题的3道题,涉及到的岗位是:通软,嵌软,测试,普通算法(岗位名中不包含AI)以及数据科学,考试内容和报考部门无关,只和岗位有关。
需要我整理的机考一周速成题单可访问文章底部左侧:阅读原文
第1题-边缘模型清洗(150分)
题目内容
云网络设备做自动化部署时,边缘设备资源很紧,要尽快把 YANG 模型装进内存,因此得先把模型文件里多余的说明文字、初始取值之类信息清掉。
请按下面的规则写一个函数:这些字段要去掉,注释必须原封不动留下来,最后把清洗结果作为字符串返回。
清洗规则
规则1:目标字段删除
要删掉的关键字:、、 删掉的跨度:从该关键字第一个字符起,一直删到本句结束用的 (这个分号也删掉) 允许跨行:关键字和分号中间可以夹着换行,匹配与删除都要跨过这些行
规则2:内容处理
被删区间:从关键字第一个字符到分号 区间里面:换行符 留下,别的字符(空格、正文、分号)都拿掉 处理后的样子: 关键字左边、还没进入区间的缩进空白,照原样留着 不管这句有没有跨行,每一行在区间里都只剩换行,行的条数不增不减
规则3:注释保护
单行注释:从 起直到这一行结束 多行注释:从 起直到 ,中间可以换行 保留方式:注释整段照抄,不能被清掉
规则4:边界定义
关键字左侧只能是:一行的开头、空格、制表符、 或 关键字右侧只能是:空格、制表符、换行或 两侧对不上:就当成普通名字,不要删(例如 )
题解
解题思路
模型文本里要删的是三个关键字各自所在的那一句,注释里的同名单词不能动,关键字左边的缩进也不能动。
用下标 从左往右扫。先看是不是 或 。块注释抄到 ,行注释抄到这一行的换行,注释内部不再找关键字。 正文里若从 起正好是 、 或 ,还要核对两侧。左侧只能是行首、空格、制表符、 或 ;右侧只能是空格、制表符、换行或 。对不上就当普通名字,例如 。 两侧都合法时,找到后面第一个 。闭区间 里只把换行符写进结果,其余字符丢掉。这样跨行的句子行数不变,关键字左边的缩进因为在区间外而保留。 当前字符都不是注释也不是合法关键字时,原样写入结果, 前进一格。
复杂度分析
时间复杂度:。每个字符最多进入结果一次,关键字匹配只在当前位置尝试常数个单词。 空间复杂度:。保存原文和清洗结果。
是输入字符串的长度。
代码实现
python代码(C++和JAVA代码见在线OJ网址)
KEYWORDS = ("required", "remark", "preset")defclean(text):# 一次扫描:注释原样抄走;正文里两侧边界都合法的关键字删到分号,换行留下 n = len(text) i = 0 out = []while i < n:if text.startswith("/*", i):# 块注释可跨行,一直抄到结束标记 end = text.find("*/", i + 2)if end < 0: out.append(text[i:])break out.append(text[i : end + 2]) i = end + 2continueif text.startswith("//", i):# 行注释抄到这一行结束,换行也属于这一行 end = text.find("\n", i)if end < 0: out.append(text[i:])break out.append(text[i : end + 1]) i = end + 1continue removed = Falsefor word in KEYWORDS:ifnot text.startswith(word, i):continue left_ok = i == 0or text[i - 1] in" \t{;\n" right = i + len(word) right_ok = right < n and text[right] in" \t;\n"ifnot (left_ok and right_ok):continue# 从关键字第一个字符删到本句分号,区间里只留换行 semi = text.find(";", right)for ch in text[i : semi + 1]:if ch == "\n": out.append(ch) i = semi + 1 removed = Truebreakif removed:continue out.append(text[i]) i += 1return"".join(out)defmain():# 整段 YANG 文本按行读入,行尾换行用连接还原 lines = []whileTrue:try: lines.append(input())except EOFError:break ans = clean("\n".join(lines))if ans.endswith("\n"): print(ans, end="")else: print(ans)if __name__ == "__main__": main()第2题-迷宫最短救援(150分)
题目背景
穿越这片迷宫、把受困公主救出来,是王子此刻要办的事。毒雾正往四处散开,因此他得用尽量少的时间赶到公主身边。挡路的不只有墙,还有守在若干区域上的怪物。
题目内容
地图已经给出。王子从出发点动身,要算出抵达公主所在格的最少移动次数。
移动规则
每次只许朝上、左、下、右迈一格,斜向跨步不算合法 跑出地图边界的格子,一律按墙处理 墙所在的格子,王子踏不上去
怪物规则
怪物上、下、左、右紧挨的那一格,没拿到宝剑时都不能踏入 地图上会出现宝剑,王子能够把它捡起来 宝剑一旦到手,王子就具备击杀怪物的能力 宝剑到手之后,怪物上、下、左、右紧挨的格子也可以进入 宝剑到手之后,王子还能踏进怪物所在格,并且只击倒脚下这一只(其余怪物仍留在原地) 宝剑若正好放在怪物紧挨的格子上,王子仍可进入该格并把剑捡走 出发时若王子本人就站在怪物紧挨的格子上,这并不妨碍他迈出下一步(下一步应当离开危险格)
策略选择
王子得在两条路线里挑更省步数的一条:
先把宝剑拿到手再去击倒怪物,步数会不会更少 或者干脆不靠近怪物、从旁边绕,步数会不会更少
题解
解题思路
没拿到宝剑时,怪物自己以及它上下左右相邻的格子都是危险格,不能踏入。宝剑格是例外,走进去就能把剑捡走。出发格即使落在危险区里,也允许作为起点,下一步再离开。拿到宝剑之后,这些危险格都能进,踩上怪物也只是击倒脚下这一只;因为持剑已经能进入任何怪物附近,搜索时不必再记录每一只怪物的死活。
先扫一遍地图,把每只怪物及其上下左右标成危险格,并记下王子的起点。 用广度优先搜索求最少步数。状态是 : 是当前行列, 为 表示还没捡到宝剑,为 表示已经持剑。 四方向扩展。目标格是墙就跳过;目标格是宝剑则新状态改为持剑。尚未持剑时,危险格不能进。 边权都是 ,队列按步数从近到远弹出。第一次到达写着 的格子,其步数就是最短路;队列空了还没到,则输出 。
复杂度分析
时间复杂度:。每个格子有持剑、未持剑两种状态,每种状态最多入队一次,每次扩展四个方向。 空间复杂度:。危险标记和距离数组都按地图大小存储。
代码实现
python代码(C++和JAVA代码见在线OJ网址)
from collections import dequedefshortest_rescue(grid):# 地图高 h、宽 w;危险格是怪物本身以及它上下左右相邻的格子 h = len(grid) w = len(grid[0]) danger = [[False] * w for _ in range(h)] start = Nonefor r in range(h):for c in range(w):if grid[r][c] == "S": start = (r, c)if grid[r][c] != "M":continue danger[r][c] = Truefor dr, dc in ((-1, 0), (1, 0), (0, -1), (0, 1)): nr, nc = r + dr, c + dcif0 <= nr < h and0 <= nc < w: danger[nr][nc] = True sr, sc = start# dist[r][c][0/1]:走到该格、且尚未持剑或已经持剑时的最少步数 dist = [[[-1] * 2for _ in range(w)] for _ in range(h)] dist[sr][sc][0] = 0 que = deque([(sr, sc, 0)])while que: r, c, sword = que.popleft()# 第一次从队列取出公主格时,步数就是最短路if grid[r][c] == "P":return dist[r][c][sword]for dr, dc in ((-1, 0), (1, 0), (0, -1), (0, 1)): nr, nc = r + dr, c + dcif nr < 0or nr >= h or nc < 0or nc >= w:continueif grid[nr][nc] == "#":continue# 走进宝剑格就变为持剑;持剑前不能踏入危险格 nxt_sword = 1if grid[nr][nc] == "W"else swordif nxt_sword == 0and danger[nr][nc]:continueif dist[nr][nc][nxt_sword] != -1:continue dist[nr][nc][nxt_sword] = dist[r][c][sword] + 1 que.append((nr, nc, nxt_sword))return-1defmain():# 第一行是行数和列数,后面 h 行是地图 h, w = map(int, input().split()) grid = [input().strip() for _ in range(h)] print(shortest_rescue(grid))if __name__ == "__main__": main()第3题-登岛最大功力(300分)
题目内容
功夫迷小明一心想闯出名声。他所处的世界里藏着一座秘岛:人留在岛上时要按时间扣掉功力,岛上冒出来的异事却能把功力补回来。
一辈子里,上岛和离岛最多各发生两次;只去一趟,或者始终不上岛,也都允许。若第一次进入、离开的时刻记成 、,第二次进入必须严格晚于第一次离开,也就是 ,两段停留不能叠在一起。
假如时刻 上岛、时刻 离岛,这一段扣掉的功力是 ,意思是每个时间单位扣 点。
秘岛上共有 起异事。第 起能补上的功力是 ,它占着闭区间 。只有整段都落在同一次停留里面,也就是 并且 ,这起异事的功力才算拿到。
结算功力等于拿到的各起异事相加,再减掉停留期间扣掉的部分。各起异事的时段可以互相交叉,也可以完全叠在同一段时间上。
请替小明定下每次上岛和离岛的时刻,使结算功力达到最大。
题解
解题思路
将所有异事出现的时刻离散化。设离散后的时间点为。
考虑一段停留区间 :
如果固定右端点 ,所有结束时间不晚于 的异事都可能产生贡献。 对于每个可能的左端点 ,维护收益:
因此,可以使用线段树维护所有左端点对应的最大收益,支持区间加和区间最大值查询。
具体步骤:
从左到右枚举离开时刻,求出每个右端点对应的最优单段停留。
当扫描到异事的结束时间 时,将它的功力 加到所有满足 的左端点上。 在线段树中查询不超过当前右端点的位置,即可得到以当前时刻为右端点时的最优区间。 从右到左枚举进入时刻,使用相同的方法求出每个左端点对应的最优单段停留。
当扫描到异事的开始时间 时,将它的功力 加到所有满足 的右端点上。 在线段树中查询不小于当前左端点的位置,即可得到以当前时刻为左端点时的最优区间。 预处理每个位置右侧能够取得的最优单段区间。
如果第一段在时刻 离岛,那么第二段的进入时刻必须严格大于 。 因此,可以直接利用后缀最优结果,将第一段与右侧的最佳第二段组合。 比较所有合法方案。
可以选择不上岛、只停留一段或者停留两段。 每一段单独产生的净功力都必须大于 。 优先选择总功力最大的方案。 如果总功力相同,则选择字典序最小的方案。 两段停留必须满足 。
使用的主要算法:
离散化 线段树 区间加 区间最大值查询 前后缀最优值合并
复杂度分析
设异事数量为 ,离散化后的不同时间点数量为,由于每起异事最多贡献两个时间点,因此有:
离散化排序的时间复杂度为:
每起异事在线段树中进行常数次区间修改,每个离散时间点进行常数次查询,每次操作的时间复杂度为 ,因此总时间复杂度为:
线段树、离散化数组以及异事信息都需要 的空间,因此空间复杂度为:
在 的数据范围下可以满足要求。
代码实现
python代码(C++和JAVA代码见在线OJ网址)
import sysclassSegTree:def__init__(self, a): self.n = len(a) self.mx = [0] * (self.n * 4) self.id = [0] * (self.n * 4) self.lazy = [0] * (self.n * 4) self.build(1, 0, self.n - 1, a)defbuild(self, o, l, r, a):if l == r: self.mx[o] = a[l] self.id[o] = lelse: m = (l + r) // 2 self.build(o*2, l, m, a) self.build(o*2+1, m+1, r, a) self.pushup(o)defpushup(self, o):if self.mx[o*2] >= self.mx[o*2+1]: self.mx[o] = self.mx[o*2] self.id[o] = self.id[o*2]else: self.mx[o] = self.mx[o*2+1] self.id[o] = self.id[o*2+1]defadd(self, o, l, r, ql, qr, v):if ql <= l and r <= qr: self.mx[o] += v self.lazy[o] += vreturn m = (l+r)//2if ql <= m: self.add(o*2, l, m, ql, qr, v)if qr > m: self.add(o*2+1, m+1, r, ql, qr, v) self.pushup(o)defquery(self, o, l, r, ql, qr):if ql <= l and r <= qr:return self.mx[o], self.id[o] m = (l+r)//2 ans = (-10**30, -1)if ql <= m: ans = self.query(o*2, l, m, ql, qr)if qr > m: b = self.query(o*2+1, m+1, r, ql, qr)if b[0] > ans[0] or (b[0] == ans[0] and b[1] < ans[1]): ans = breturn ansdefsolve(): p, c = map(int, sys.stdin.readline().split()) segs = [] xs = []for _ in range(p): a, b, w = map(int, sys.stdin.readline().split()) segs.append((a, b, w)) xs += [a, b] xs = sorted(set(xs)) mp = {x:i for i,x in enumerate(xs)} n = len(xs) by_r = [[] for _ in range(n)] by_l = [[] for _ in range(n)]for a,b,w in segs: by_r[mp[b]].append((mp[a], w)) by_l[mp[a]].append((mp[b], w))# 求每个右端点的最佳区间 tree = SegTree([c*x for x in xs]) left_best = [None]*nfor i in range(n):for a,w in by_r[i]: tree.add(1,0,n-1,0,a,w) val,pos = tree.query(1,0,n-1,0,i) val -= c*xs[i]if val > 0: left_best[i] = (val, xs[pos], xs[i])# 求每个左端点开始的最佳区间 tree = SegTree([-c*x for x in xs]) right_best = [None]*nfor i in range(n-1,-1,-1):for b,w in by_l[i]: tree.add(1,0,n-1,b,n-1,w) val,pos = tree.query(1,0,n-1,i,n-1) val += c*xs[i]if val > 0: right_best[i] = (val, xs[i], xs[pos]) suf = [None]*(n+1)for i in range(n-1,-1,-1): suf[i] = suf[i+1]if right_best[i]:if suf[i] isNoneor right_best[i][0] > suf[i][0]: suf[i] = right_best[i] ans = (0, [])for i in range(n):if left_best[i]: cur = left_best[i]if cur[0] > ans[0]: ans = (cur[0], [cur])if i+1 < n and suf[i+1]: total = cur[0] + suf[i+1][0]if total > ans[0]: ans = (total, [cur, suf[i+1]]) print(ans[0])ifnot ans[1]: print("NA")else:for x in ans[1]: print(str(x[1]) + "," + str(x[2]))if __name__ == "__main__": solve()