夜雨聆风学习资料网

ARTICLE · 1065519

华为机考非AI方向9月23日笔试题与解析

华为机考非AI方向9月23日笔试题与解析

写在前面

本次给大家带来2026年9月23日华为非AI方向笔试题的3道题,涉及到的岗位是:通软,嵌软,测试,普通算法(岗位名中不包含AI)以及数据科学,考试内容和报考部门无关,只和岗位有关。

需要我整理的机考一周速成题单可访问文章底部左侧:阅读原文

题号
题目
难度(对标leetcode)
核心做法
1
边缘模型清洗
中等
模拟
2
迷宫最短救援
中等
BFS
3
登岛最大功力
困难
线段树

第1题-边缘模型清洗(150分)

题目内容

云网络设备做自动化部署时,边缘设备资源很紧,要尽快把 YANG 模型装进内存,因此得先把模型文件里多余的说明文字、初始取值之类信息清掉。

请按下面的规则写一个函数:这些字段要去掉,注释必须原封不动留下来,最后把清洗结果作为字符串返回。

清洗规则

规则1:目标字段删除

  • 要删掉的关键字:
  • 删掉的跨度:从该关键字第一个字符起,一直删到本句结束用的 (这个分号也删掉)
  • 允许跨行:关键字和分号中间可以夹着换行,匹配与删除都要跨过这些行

规则2:内容处理

  • 被删区间:从关键字第一个字符到分号 
  • 区间里面:换行符  留下,别的字符(空格、正文、分号)都拿掉
  • 处理后的样子:
    • 关键字左边、还没进入区间的缩进空白,照原样留着
    • 不管这句有没有跨行,每一行在区间里都只剩换行,行的条数不增不减

规则3:注释保护

  • 单行注释:从  起直到这一行结束
  • 多行注释:从  起直到 ,中间可以换行
  • 保留方式:注释整段照抄,不能被清掉

规则4:边界定义

  • 关键字左侧只能是:一行的开头、空格、制表符、 或 
  • 关键字右侧只能是:空格、制表符、换行或 
  • 两侧对不上:就当成普通名字,不要删(例如 

题解

解题思路

模型文本里要删的是三个关键字各自所在的那一句,注释里的同名单词不能动,关键字左边的缩进也不能动。

  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 - 1in" \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分)

题目背景

穿越这片迷宫、把受困公主救出来,是王子此刻要办的事。毒雾正往四处散开,因此他得用尽量少的时间赶到公主身边。挡路的不只有墙,还有守在若干区域上的怪物。

题目内容

地图已经给出。王子从出发点动身,要算出抵达公主所在格的最少移动次数。

移动规则

  • 每次只许朝上、左、下、右迈一格,斜向跨步不算合法
  • 跑出地图边界的格子,一律按墙处理
  • 墙所在的格子,王子踏不上去

怪物规则

  • 怪物上、下、左、右紧挨的那一格,没拿到宝剑时都不能踏入
  • 地图上会出现宝剑,王子能够把它捡起来
  • 宝剑一旦到手,王子就具备击杀怪物的能力
  • 宝剑到手之后,怪物上、下、左、右紧挨的格子也可以进入
  • 宝剑到手之后,王子还能踏进怪物所在格,并且只击倒脚下这一只(其余怪物仍留在原地)
  • 宝剑若正好放在怪物紧挨的格子上,王子仍可进入该格并把剑捡走
  • 出发时若王子本人就站在怪物紧挨的格子上,这并不妨碍他迈出下一步(下一步应当离开危险格)

策略选择

王子得在两条路线里挑更省步数的一条:

  • 先把宝剑拿到手再去击倒怪物,步数会不会更少
  • 或者干脆不靠近怪物、从旁边绕,步数会不会更少

题解

解题思路

没拿到宝剑时,怪物自己以及它上下左右相邻的格子都是危险格,不能踏入。宝剑格是例外,走进去就能把剑捡走。出发格即使落在危险区里,也允许作为起点,下一步再离开。拿到宝剑之后,这些危险格都能进,踩上怪物也只是击倒脚下这一只;因为持剑已经能进入任何怪物附近,搜索时不必再记录每一只怪物的死活。

  1. 先扫一遍地图,把每只怪物及其上下左右标成危险格,并记下王子的起点。
  2. 用广度优先搜索求最少步数。状态是  是当前行列, 为  表示还没捡到宝剑,为  表示已经持剑。
  3. 四方向扩展。目标格是墙就跳过;目标格是宝剑则新状态改为持剑。尚未持剑时,危险格不能进。
  4. 边权都是 ,队列按步数从近到远弹出。第一次到达写着  的格子,其步数就是最短路;队列空了还没到,则输出 

复杂度分析

  • 时间复杂度:。每个格子有持剑、未持剑两种状态,每种状态最多入队一次,每次扩展四个方向。
  • 空间复杂度:。危险标记和距离数组都按地图大小存储。

代码实现

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 ((-10), (10), (0-1), (01)):                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 ((-10), (10), (0-1), (01)):            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分)

题目内容

功夫迷小明一心想闯出名声。他所处的世界里藏着一座秘岛:人留在岛上时要按时间扣掉功力,岛上冒出来的异事却能把功力补回来。

一辈子里,上岛和离岛最多各发生两次;只去一趟,或者始终不上岛,也都允许。若第一次进入、离开的时刻记成 ,第二次进入必须严格晚于第一次离开,也就是 ,两段停留不能叠在一起。

假如时刻  上岛、时刻  离岛,这一段扣掉的功力是 ,意思是每个时间单位扣  点。

秘岛上共有  起异事。第  起能补上的功力是 ,它占着闭区间 。只有整段都落在同一次停留里面,也就是  并且 ,这起异事的功力才算拿到。

结算功力等于拿到的各起异事相加,再减掉停留期间扣掉的部分。各起异事的时段可以互相交叉,也可以完全叠在同一段时间上。

请替小明定下每次上岛和离岛的时刻,使结算功力达到最大。

题解

解题思路

将所有异事出现的时刻离散化。设离散后的时间点为

考虑一段停留区间 

  • 如果固定右端点 ,所有结束时间不晚于  的异事都可能产生贡献。
  • 对于每个可能的左端点 ,维护收益:

因此,可以使用线段树维护所有左端点对应的最大收益,支持区间加和区间最大值查询。

具体步骤:

  1. 从左到右枚举离开时刻,求出每个右端点对应的最优单段停留。

    • 当扫描到异事的结束时间  时,将它的功力 加到所有满足 的左端点上。
    • 在线段树中查询不超过当前右端点的位置,即可得到以当前时刻为右端点时的最优区间。
  2. 从右到左枚举进入时刻,使用相同的方法求出每个左端点对应的最优单段停留。

    • 当扫描到异事的开始时间  时,将它的功力  加到所有满足  的右端点上。
    • 在线段树中查询不小于当前左端点的位置,即可得到以当前时刻为左端点时的最优区间。
  3. 预处理每个位置右侧能够取得的最优单段区间。

    • 如果第一段在时刻  离岛,那么第二段的进入时刻必须严格大于 
    • 因此,可以直接利用后缀最优结果,将第一段与右侧的最佳第二段组合。
  4. 比较所有合法方案。

    • 可以选择不上岛、只停留一段或者停留两段。
    • 每一段单独产生的净功力都必须大于 
    • 优先选择总功力最大的方案。
    • 如果总功力相同,则选择字典序最小的方案。
    • 两段停留必须满足 

使用的主要算法:

  • 离散化
  • 线段树
  • 区间加
  • 区间最大值查询
  • 前后缀最优值合并

复杂度分析

设异事数量为 ,离散化后的不同时间点数量为,由于每起异事最多贡献两个时间点,因此有:

离散化排序的时间复杂度为:

每起异事在线段树中进行常数次区间修改,每个离散时间点进行常数次查询,每次操作的时间复杂度为 ,因此总时间复杂度为:

线段树、离散化数组以及异事信息都需要  的空间,因此空间复杂度为:

在  的数据范围下可以满足要求。

代码实现

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(10, 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[0or (b[0] == ans[0and 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()
刷题练习:CodeFun2000.com

相关学习资料