2026华为OD面试题001:两个字符串间的最短路径问题

题目描述

给你两个字符串 A 和 B,比如 A = "ABCABBA"B = "CBABAC"

想象一个棋盘,横轴是 A 的字符,纵轴是 B 的字符。从左上角 (0, 0) 走到右下角 (m, n),每次只能往右、往下,或者斜着往右下走。

  • 往右走:消耗 B 的一个字符,代价 1。
  • 往下走:消耗 A 的一个字符,代价 1。
  • 斜着走:只有当前两个字符相同才能走,代价 1,但一下搞定两个方向。

问题是:从起点到终点的最短距离是多少?

讲个故事:小明的最短通勤路

小明住在 (0, 0),公司在 (m, n)。他有三条路可以选:

  • 平路:往东走一格,或者往南走一格,各花 1 块钱。
  • 捷径:如果路口招牌一样,可以走对角线,花 1 块钱但等于同时走了东和南。

小明当然想多走捷径。

能走多少条捷径?取决于 A 和 B 里有多少字符能按顺序对上。这就是最长公共子序列。

核心原理:为什么答案是 m + n - LCS?

这道题看着像在网格里找路,其实就是**最长公共子序列(LCS)**的换皮题。答案等于 len(A) + len(B) - LCS(A, B)

假设我们找到了一条最长公共子序列,长度为 L

  • 这 L 个字符可以走斜边,花 L 块钱。
  • A 还剩下 m - L 个字符,必须往下走,花 m - L 块钱。
  • B 还剩下 n - L 个字符,必须往右走,花 n - L 块钱。

总花费 = L + (m - L) + (n - L) = m + n - L

所以只要求出 LCS 长度,答案就出来了。

拿样例验证一下:A = "ABCABBA" 长度 7,B = "CBABAC" 长度 6,LCS 长度是 4(比如 "BABA" 或者 "CABA")。最短距离就是 7 + 6 - 4 = 9,和题目给的结果一致。

怎么求 LCS?

用动态规划。设 dp[i][j] 表示 A[0..i-1]B[0..j-1] 的最长公共子序列长度。

转移方程:

  • 如果 A[i-1] == B[j-1]dp[i][j] = dp[i-1][j-1] + 1
  • 否则:dp[i][j] = max(dp[i-1][j], dp[i][j-1])

初始化:dp[0][j] = 0dp[i][0] = 0

代码实现

C 语言

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int minPath(char *A, char *B) {
    int m = strlen(A), n = strlen(B);
    int **dp = (int **)malloc((m + 1) * sizeof(int *));
    for (int i = 0; i <= m; i++) {
        dp[i] = (int *)calloc(n + 1, sizeof(int));
    }
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (A[i - 1] == B[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = dp[i - 1][j] > dp[i][j - 1] ? dp[i - 1][j] : dp[i][j - 1];
            }
        }
    }
    int result = m + n - dp[m][n];
    for (int i = 0; i <= m; i++) free(dp[i]);
    free(dp);
    return result;
}

int main() {
    char A[10005], B[10005];
    scanf("%s %s", A, B);
    printf("%d\n", minPath(A, B));
    return 0;
}

C++

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int minPath(const string& A, const string& B) {
    int m = A.size(), n = B.size();
    vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (A[i - 1] == B[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    return m + n - dp[m][n];
}

int main() {
    string A, B;
    cin >> A >> B;
    cout << minPath(A, B) << endl;
    return 0;
}

Java

java 复制代码
import java.util.Scanner;

public class Main {
    public static int minPath(String A, String B) {
        int m = A.length(), n = B.length();
        int[][] dp = new int[m + 1][n + 1];
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (A.charAt(i - 1) == B.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return m + n - dp[m][n];
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String A = sc.next();
        String B = sc.next();
        System.out.println(minPath(A, B));
    }
}

JavaScript

javascript 复制代码
function minPath(A, B) {
    const m = A.length, n = B.length;
    const dp = Array.from({length: m + 1}, () => new Array(n + 1).fill(0));
    for (let i = 1; i <= m; i++) {
        for (let j = 1; j <= n; j++) {
            if (A[i - 1] === B[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    return m + n - dp[m][n];
}

// 输入处理
const readline = require('readline');
const rl = readline.createInterface({input: process.stdin});
rl.on('line', (line) => {
    const [A, B] = line.trim().split(/\s+/);
    console.log(minPath(A, B));
    rl.close();
});

Python

python 复制代码
def min_path(A, B):
    m, n = len(A), len(B)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if A[i - 1] == B[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
    return m + n - dp[m][n]

A, B = input().split()
print(min_path(A, B))

空间优化(进阶)

如果你发现内存不够,可以用滚动数组把空间复杂度压到 O(min(m, n))

python 复制代码
def min_path_optimized(A, B):
    if len(A) < len(B):
        A, B = B, A
    m, n = len(A), len(B)
    prev = [0] * (n + 1)
    curr = [0] * (n + 1)
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if A[i - 1] == B[j - 1]:
                curr[j] = prev[j - 1] + 1
            else:
                curr[j] = max(prev[j], curr[j - 1])
        prev, curr = curr, prev
    return m + n - prev[n]

复杂度分析

  • 时间复杂度:O(m * n)
  • 空间复杂度:O(m * n),滚动数组优化后是 O(min(m, n))

总结一下

这道题的关键不是背网格图,而是看出:

  • 斜边 = 公共子序列里的一个字符
  • 最短路径 = 总长度 - 最长公共子序列长度

下次看到"字符串网格""最短路径""匹配"这类关键词,脑子里先蹦出 LCS。

你平时做 OD 题最怕哪种题型?欢迎在评论区聊聊。

相关推荐
2601_9620725518 天前
李梦娇常识4600问|题库|打印版
sql·华为od·华为·c#·华为云·.net·harmonyos
HEADKON21 天前
Larotretinib拉罗替尼治NTRK融合实体瘤,神经系统反应多为一过性
华为od
无限码力1 个月前
2026最新华为OD机试新系统 机考真题考点分类 + 备考策略
华为od·华为od机考·华为od机试·华为od机试新系统真题·华为od机试备考策略·华为od机考真题考点详解
TechPioneer_lp1 个月前
就业指导|中九非科班毕业,华为 OD 做 Java 后端想转 C++,能找到深度学习挂钩的岗工作吗?
java·c++·华为od·华为·就业指导·校招指导
无限码力1 个月前
华为OD机试真题 新系统-等距二进制判断(C/C++/Py/Java/Js/Go)
华为od·华为od机考·华为od机试真题·华为od机试·华为od机试题库·华为od机试新系统真题
largecode2 个月前
怎么让手机显示公司名?来电显示公司名称认证实现品牌外显
linux·ubuntu·华为od·华为·智能手机·华为云·harmonyos
无限码力2 个月前
华为OD新系统机试真题 - 日志文件异常检测
华为od·华为od机考·华为od机试真题·华为od上机考试真题·华为od机考真题·华为od新系统机试真题·华为od新系统机试
无限码力2 个月前
2026最新华为OD新系统机试解析 + 最新题库 + 备考策略
华为od·华为od机考·华为od机试真题·华为od机试·华为od新系统机试真题·2026华为od新系统机试题库·华为od机考题库
无限码力3 个月前
华为OD机试真题 新系统 - API请求日志去重分析 (C/C++/Py/Java/Js/Go)
华为od·华为od机试真题·华为od上机考试真题·华为od机考真题·华为od4月15号机试真题·华为od新系统真题