题目描述与示例

题目

小明负责维护项目下的代码,需要查找出重复代码,用以支撑后续的代码优化,请你帮助小明找出重复的代码。 重复代码查找方法:以字符串形式给出两行代码text1text2(字符串长度1 < len(text1),len(text2) <= 100,由英文字母、数字和空格组成),找出两行代码中的最长公共子串。如果不存在公共子串,返回空字符串。

注意子串是连续的

题目练习网址:https://www.algomooc.com/problem/P3440

输入

输入的参数text1text2分别表示两行代码

输出

输出任一最长公共子串。

示例一

输入

hello123world
hello123abc4

输出

hello123

示例二

输入

private_void_method
public_void_method

输出

_void_method

示例三

输入

hiworld
hiweb

输出

hiw

解题思路

注意:本题和LC718. 最长重复子数组几乎完全一致。唯一区别在于,本题要计算的不是最长公共子串的长度,而是要输出最长公共子串本身

这是一个典型的dp问题。我们考虑动态规划三部曲:

  1. dp数组的含义是什么?
  • dp数组是一个大小为(N+1)*(M+1)的二维矩阵,dp[i][j]表示 text1i 个元素和 text2j 个元素的公共的、长度最长的连续子串的长度。
  1. 动态转移方程是什么?
  • 如果发现 text1 的当前元素,即位置为 i-1 的元素,与 text2 的当前元素即位置为 j-1 的元素相同。此时,找到了一个公共元素,公共的、长度最长的子串的长度加 1
if text1[i-1] == text2[j-1]:
dp[i][j] = dp[i - 1][j - 1] + 1
  1. dp数组如何初始化?
  • dp[0][0] 表示 text10 个元素和 text20 个元素的公共的、长度最长的子串的长度,此时公共的、长度最长的子串的长度为 0
  • text1 或者 text2 没有字符时,公共的、长度最长的子串的长度都为 0
dp = [[0] * (n + 1) for _ in range(m + 1)]

dp[0][0] = 0

for i in range(1, m+1):
dp[i][0] = 0 for j in range(1, n+1):
dp[0][j] = 0

考虑完上述问题后,代码其实呼之欲出了。

代码

Python

# 欢迎来到「欧弟算法 - 华为OD全攻略」,收录华为OD题库、面试指南、八股文与学员案例!
# 地址:https://www.odalgo.com text1 = input()
text2 = input() # 获取 text1 的长度
m = len(text1) # 获取 text2 的长度
n = len(text2) # 设置数组 dp,用来存储 text1 和 text2 公共的、长度最长的子串的长度
# dp[0][0] 表示 text1 前 0 个元素和 text2 前 0 个元素的公共的、长度最长的子串的长度
# dp[2][3] 表示 text1 前 2 个元素和 text2 前 3 个元素的公共的、长度最长的子串的长度
# dp[i][j] 表示 text1 前 i 个元素和 text2 前 j 个元素的公共的、长度最长的子串的长度
# 前 i 个元素的区间为 [0, i-1]
# dp[m][n] 表示 text1 前 m 个元素和 text2 前 n 个元素的公共的、长度最长的子串的长度
# 前 m 个元素的表示区间为 [0, m],前 n 个元素的表示区间为 [0, n]
# 因此,dp 数组的长度为 m + 1 和 n + 1
dp = [[0] * (n + 1) for _ in range(m + 1)] # maxLength 表示 dp 数组中的最大值
maxLength = 0 # 初始化答案变量为空串
ans = "" # 1. 初始化dp数组
# dp[0][0] 表示 text1 前 0 个元素和 text2 前 0 个元素的公共的、长度最长的子串的长度
# text1 text2 也没有元素
# 因此,公共的、长度最长的子串的长度为 0
dp[0][0] = 0 # text1 没有元素 或者 text2 没有字符时,公共的、长度最长的子串的长度都为 0
for i in range(1, m+1):
dp[i][0] = 0 for j in range(1, n+1):
dp[0][j] = 0 # i 从 1 开始,直到 m 位置,遍历 text1 的前 i 个元素
for i in range(1, m+1):
# j 从 1 开始,直到 n 位置,遍历 text2 的前 j 个元素
for j in range(1, n+1): # 2. 动态转移方程
# 如果发现 text1 的当前元素,即位置为 i - 1 的元素
# 与 text2 的当前元素,即位置为 j - 1 的元素【相同】
# 此时,找到了一个公共元素,公共的、长度最长的子串的长度加 1
if text1[i - 1] == text2[j - 1]: # dp[i][j] 表示 text1 前 i 个元素和 text2 前 j 个元素的公共的、长度最长的子串的长度
# dp[i - 1][j - 1] 表示 text1 前 i - 1 个元素和 text2 前 j - 1 个元素的公共的、长度最长的子串的长度
dp[i][j] = dp[i - 1][j - 1] + 1 # 更新最长的子串的长度
if maxLength < dp[i][j]:
maxLength = dp[i][j]
# 最长子串长度为maxLength,对于text1而言,
# 当前结束位置为i,当前开始位置为i-maxLength
# 这里换成text2[j-maxLength: j]也是一样的
ans = text1[i-maxLength: i] # 返回结果
print(ans)

Java

import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String text1 = scanner.nextLine();
String text2 = scanner.nextLine();
int m = text1.length();
int n = text2.length();
int[][] dp = new int[m + 1][n + 1];
int maxLength = 0;
String ans = ""; for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
if (maxLength < dp[i][j]) {
maxLength = dp[i][j];
ans = text1.substring(i - maxLength, i);
}
}
}
} System.out.println(ans);
}
}

C++

#include <iostream>
#include <string>
#include <vector> using namespace std; int main() {
string text1, text2;
getline(cin, text1);
getline(cin, text2); // 获取 text1 的长度
int m = text1.length(); // 获取 text2 的长度
int n = text2.length(); // 设置数组 dp,用来存储 text1 和 text2 公共的、长度最长的子串的长度
// dp[0][0] 表示 text1 前 0 个元素和 text2 前 0 个元素的公共的、长度最长的子串的长度
// dp[2][3] 表示 text1 前 2 个元素和 text2 前 3 个元素的公共的、长度最长的子串的长度
// dp[i][j] 表示 text1 前 i 个元素和 text2 前 j 个元素的公共的、长度最长的子串的长度
// 前 i 个元素的区间为 [0, i-1]
// dp[m][n] 表示 text1 前 m 个元素和 text2 前 n 个元素的公共的、长度最长的子串的长度
// 前 m 个元素的表示区间为 [0, m],前 n 个元素的表示区间为 [0, n]
// 因此,dp 数组的长度为 m + 1 和 n + 1
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); // maxLength 表示 dp 数组中的最大值
int maxLength = 0; // 初始化答案变量为空串
string ans = ""; // 1. 初始化dp数组
// dp[0][0] 表示 text1 前 0 个元素和 text2 前 0 个元素的公共的、长度最长的子串的长度
// text1 text2 也没有元素
// 因此,公共的、长度最长的子串的长度为 0
dp[0][0] = 0; // text1 没有元素 或者 text2 没有字符时,公共的、长度最长的子串的长度都为 0
for (int i = 1; i <= m; ++i) {
dp[i][0] = 0;
} for (int j = 1; j <= n; ++j) {
dp[0][j] = 0;
} // i 从 1 开始,直到 m 位置,遍历 text1 的前 i 个元素
for (int i = 1; i <= m; ++i) {
// j 从 1 开始,直到 n 位置,遍历 text2 的前 j 个元素
for (int j = 1; j <= n; ++j) {
// 2. 动态转移方程
// 如果发现 text1 的当前元素,即位置为 i - 1 的元素
// 与 text2 的当前元素,即位置为 j - 1 的元素【相同】
// 此时,找到了一个公共元素,公共的、长度最长的子串的长度加 1
if (text1[i - 1] == text2[j - 1]) {
// dp[i][j] 表示 text1 前 i 个元素和 text2 前 j 个元素的公共的、长度最长的子串的长度
// dp[i - 1][j - 1] 表示 text1 前 i - 1 个元素和 text2 前 j - 1 个元素的公共的、长度最长的子串的长度
dp[i][j] = dp[i - 1][j - 1] + 1; // 更新最长的子串的长度
if (maxLength < dp[i][j]) {
maxLength = dp[i][j];
// 最长子串长度为maxLength,对于text1而言,
// 当前结束位置为i,当前开始位置为i-maxLength
// 这里换成text2[j-maxLength: j]也是一样的
ans = text1.substr(i - maxLength, maxLength);
}
}
}
} // 返回结果
cout << ans << endl; return 0;
}

C

#include <stdio.h>
#include <string.h>
#include <stdlib.h> char* findLongestCommonSubstring(char* text1, char* text2) {
int m = strlen(text1);
int n = strlen(text2); // 动态分配 dp 数组
int** dp = (int**)malloc((m + 1) * sizeof(int*));
for (int i = 0; i <= m; i++) {
dp[i] = (int*)calloc(n + 1, sizeof(int));
} int maxLength = 0; // 记录最长公共子串的长度
int endIndex = 0; // 记录最长子串在 text1 中的结束索引 // 遍历 text1 和 text2 的所有字符
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (text1[i - 1] == text2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1; if (dp[i][j] > maxLength) {
maxLength = dp[i][j];
endIndex = i;
}
}
}
} // 提取最长公共子串
char* ans = (char*)malloc((maxLength + 1) * sizeof(char));
strncpy(ans, text1 + endIndex - maxLength, maxLength);
ans[maxLength] = '\0'; // 释放动态分配的内存
for (int i = 0; i <= m; i++) {
free(dp[i]);
}
free(dp); return ans;
} int main() {
char text1[1001], text2[1001];
scanf("%s %s", text1, text2); char* result = findLongestCommonSubstring(text1, text2);
printf("%s\n", result); free(result); // 释放返回字符串的内存
return 0;
}

Node JavaScript

function findLongestCommonSubstring(text1, text2) {
const m = text1.length;
const n = text2.length; // 初始化 dp 数组
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0)); let maxLength = 0; // 记录最长公共子串的长度
let endIndex = 0; // 记录最长子串在 text1 中的结束索引 // 遍历 text1 和 text2 的所有字符
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (text1[i - 1] === text2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1; if (dp[i][j] > maxLength) {
maxLength = dp[i][j];
endIndex = i;
}
}
}
} // 提取最长公共子串
return text1.slice(endIndex - maxLength, endIndex);
} // 测试主函数
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
}); rl.question('', (line1) => {
rl.question('', (line2) => {
console.log(findLongestCommonSubstring(line1, line2));
rl.close();
});
});

Go

package main

import (
"bufio"
"fmt"
"os"
) func findLongestCommonSubstring(text1, text2 string) string {
m, n := len(text1), len(text2) // 初始化 dp 数组
dp := make([][]int, m+1)
for i := range dp {
dp[i] = make([]int, n+1)
} maxLength := 0 // 记录最长公共子串的长度
endIndex := 0 // 记录最长子串在 text1 中的结束索引 // 遍历 text1 和 text2 的所有字符
for i := 1; i <= m; i++ {
for j := 1; j <= n; j++ {
if text1[i-1] == text2[j-1] {
dp[i][j] = dp[i-1][j-1] + 1 if dp[i][j] > maxLength {
maxLength = dp[i][j]
endIndex = i
}
}
}
} // 提取最长公共子串
return text1[endIndex-maxLength : endIndex]
} func main() {
reader := bufio.NewReader(os.Stdin)
text1, _ := reader.ReadString('\n')
text2, _ := reader.ReadString('\n') // 去掉末尾的换行符
text1 = text1[:len(text1)-1]
text2 = text2[:len(text2)-1] result := findLongestCommonSubstring(text1, text2)
fmt.Println(result)
}

时空复杂度

时间复杂度:O(MN)。dp过程需要经历双重循环。

空间复杂度:O(MN)。二维dp数组所占据的空间。

MN分别为text1text2的长度。

华为od机考2025A卷真题 -寻找重复代码的更多相关文章

  1. 华为OD机试题

    """最长回文字符串问题"""# 说明:方法很多,这个是最简单,也是最容易理解的一个,利用了动态规化.# 先确定回文串的右边界i,然后以右边 ...

  2. 2020年最新阿里、字节、腾讯、京东等一线大厂高频面试(Android岗)真题合集,面试轻松无压力

    本文涵盖了阿里巴巴.腾讯.字节跳动.京东.华为等大厂的Android面试真题,不管你是要面试大厂还是普通的互联网公司,这些面试题对你肯定是有帮助的,毕竟大厂一定是行发展的标杆,很多公司的面试官同样会研 ...

  3. 软考之信息安全工程师(包含2016-2018历年真题详解+官方指定教程+VIP视频教程)

    软考-中级信息安全工程师2016-2018历年考试真题以及详细答案,同时含有信息安全工程师官方指定清华版教程.信息安全工程师高清视频教程.持续更新后续年份的资料.请点赞!!请点赞!!!绝对全部货真价实 ...

  4. PMP全真模拟题真题試題含答案解析 2019年下半年PMP考試适用 PMP中文文对照试题 【香港台灣地區PMP考試也可用】

    PMP全真模拟题真题试题 含答案解析 2019年下半年PMP考试适用 PMP中文文对照试题 [香港台灣地區PMP考試也可用]PMP全真模擬題真題試題 含答案解析 2019年下半年PMP考試适用 PMP ...

  5. 肖sir__ 代码题 ___华为od练习

    www.online1987.com 这个网站,有概率看到机考原题,后续内招,这个网站做到了原题

  6. 数百道BAT等大厂最新Python面试真题,学到你手软!

    春招临近,无论是要找工作的准毕业生,还是身在职场想要提升自己的程序员,提升自己的算法内功心法.提升 Python 编程能力,总是大有裨益的.今天,小编发现了一份好资源:Python 实现的面试题集锦! ...

  7. 华为OD两轮技术面试

    华为OD面试1性格测试选积极向上的选项,注意,性格测试也会挂人,我一个朋友性格测试就没过.2机试 一道变成题目 1h 用例60%通过即可任给一个数组,元素有20M,1T,300G之类的,其中1T=10 ...

  8. 秋招如何抱佛脚?2022最新大厂Java面试真题合集(附答案

    2022秋招眼看着就要来了,但是离谱的是,很多同学最近才想起来还有秋招这回事,所以纷纷临时抱佛脚,问我有没有什么快速磨枪的方法, 我的回答是:有! 说起来,临阵磨枪没有比背八股文更靠谱的了,很多人对这 ...

  9. 你只是看起来很努力(只是做了一遍真题,草草的对了一遍答案,然后冲出自习室继续她学生会的事情了,骗自己更容易)good——想起了自己在六大时候的无奈

    (转)你只是看起来很努力一次上课,一个女孩子垂头丧气的跟我说,老师,我考了四次四级,还没过,究竟是为什么. 我说,你真题做了吗?单词背了吗?她拿出已经翻破了的真题,跟我说,你讲的所有的题目我连答案都记 ...

  10. 第四届蓝桥杯 c/c++真题

    第四届蓝桥杯 c/c++真题 <1>高斯日记 问题 大数学家高斯有个好习惯:无论如何都要记日记. 他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210 后来人们 ...

随机推荐

  1. python包学习:-了解

    本节先做一些了解. numpy 参考:NumPy使用 NumPy 教程 NumPy是Python中科学计算的基础包.它是一个Python库,提供多维数组对象,各种派生对象(如掩码数组和矩阵),以及用于 ...

  2. 拥有自己的解析器(C#实现LALR(1)语法解析器和miniDFA词法分析器的生成器)

    拥有自己的解析器(C#实现LALR(1)语法解析器和miniDFA词法分析器的生成器) 参考lex和yacc的输入格式,参考虎书<现代编译原理-C语言描述>的算法,不依赖第三方库,大力整合 ...

  3. 小程序image图片缩小不变形

    使用mode =aspectFill 值 说明 scaleToFill 缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素 图片会变形 aspectFit 缩放模式,保持纵横 ...

  4. Django-Admin和第三方插件Xadmin

    Admin django内置了一个强大的组件叫Admin,提供给网站管理员快速开发运营后台的管理站点. 站点文档: https://docs.djangoproject.com/zh-hans/2.2 ...

  5. 还堵在高速路上吗?带你进入Scratch世界带你飞

    国庆假期高速路的风景 国庆假期正式启动人从众模式,无论是高速公路还是景区,不管是去程还是回程,每一次都堪称经典. 一些网友在经历漫长的拥堵后 哭笑不得地表示 "假期都在堵车中度过了" ...

  6. 【Unity】URP中的UGUIShader实现

    [Unity]URP 中的 UGUIShader 实现 参考官方 Shader 代码实现: https://github.com/TwoTailsGames/Unity-Built-in-Shader ...

  7. 【COM3D2Mod 制作教程(4)】实战!制作身体部分(上)

    [COM3D2Mod 制作教程(4)]实战!制作身体部分(上) 教程介绍 现在正式进入实战教程环节,我会以我的实际制作过程详尽的教授每个细节,也因此受限于篇幅大小,"实战!制作身体部分&qu ...

  8. 如何让N1盒子、机顶盒开机从优盘启动进入刷机流程

    疑难解答加微信机器人,给它发:进群,会拉你进入八米交流群 机器人微信号:bamibot 简洁版教程访问:https://bbs.8miyun.cn 准备阶段 1.下载我的从优盘启动的工具包 2.确认你 ...

  9. C# 之事件及event关键字存在的意义

    总结:event关键字的作用,用于不公开发布器中委托对象实例,对事件委托对象进行保护,禁止外部调用. 1.C#事件举例说明 1 //事件及event关键字存在的意义 2 class Program 3 ...

  10. 李沐动手学深度学习V2-chapter_convolutional-modern

    李沐动手学深度学习V2 文章内容说明 本文主要是自己学习过程中的随手笔记,需要自取 课程参考B站:https://space.bilibili.com/1567748478?spm_id_from=3 ...