字符补全(C++/Py/Java/Js/Go)题解
华为笔试真题 7月15号 非AI方向第三题 300分题型
题目内容
给定一个目标字符串 TTT 和一个源字符串 SSS,请你找出需要在 SSS 中最少插入 多少个字符(可以在任意位置插入),才能使得 TTT 成为 SSS 的子序列。
注意:
- 子序列定义 :对于一个字符串 UUU,如果字符串 VVV 可以通过删除 UUU 中的一些字符(可以删除 000 个或多个,不改变剩余字符的相对顺序)得到,则称 VVV 是 UUU 的子序列。
- 例如:在 "acbdacbdacbd" 中,"ababab"、"acacac"、"adadad"、"cdcdcd"、"abcdabcdabcd"等都是其子序列。
- 子序列中的字符在原字符串中不需要连续出现,但必须保持原有的相对顺序。
- 例如:"ababab" 是 "axbyaxbyaxby" 的子序列,因为 'aaa' 在 'bbb' 之前出现。
- 只能插入字符,不能删除或修改现有字符。
- 插入的字符必须是 TTT 中有的字符。
约束条件:
- 1≤∣S∣,∣T∣≤25001 \le |S|, |T| \le 25001≤∣S∣,∣T∣≤2500
- SSS 和 TTT 只包含小写字母 ′a′'a'′a′~′z′'z'′z′
输入描述
第一行输入目标字符串 TTT
第二行输入源字符串 SSS
输出描述
输出最少需要插入的字符数量
样例1
输入
abc
ac
输出
1
说明
在 'ccc' 前面插入 'bbb',得到 "abcabcabc",所以需要插入 111 个字符。这是最典型的情况,展示了当目标字符串只比源字符串多一个字符时如何处理。
样例2
输入
abc
xyz
输出
3
说明
源字符串 SSS 中没有目标字符串 TTT 的任何字符,需要插入 "abcabcabc" 全部 333 个字符。这是边界情况,展示了当两个字符串完全不相交时如何处理。
样例3
输入
aaab
ab
输出
2
说明
源字符串 SSS 只有 "ababab",而目标字符串 TTT 有三个 'aaa' 和一个 'bbb'。可以匹配一个 'aaa' 和一个 'bbb',但还需要插入两个 'aaa'。这是特殊情况,展示了重复字符的处理。
题解
思路
思路:动态规划
- 本题其实可以直接转换为求
S T的最长公共子序列,要插入的字母数量就为T.size() - 最大公共子序列长度。 - 求最长子序列使用对应模板即可,定义
dp[i][j]数组,表示T 前 i 个字符和 S 前 j 个字符*的最长公共子序列长度 - 状态转移
- 字符相同
T[i-1] == S[j-1], 对应执行dp[i][j] = dp[i - 1][j - 1] + 1; - 字符不相同时,执行
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
- 字符相同
- 最终结果即为
T.size() - dp[n][m], 总体时间复杂度为O(nm)
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
string t,s;
cin >> t;
cin >> s;
int n = t.size();
int m = s.size();
// dp[i][]j T 前 i 个字符 和 S 前 j 个字符 的最长公共子序列长度。
vector<vector<int>> dp(n+1,vector<int>(m+1,0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (t[i - 1] == s[j - 1]) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}
}
}
int ans = n - dp[n][m];
cout << ans;
return 0;
}
java
java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String t = sc.next();
String s = sc.next();
int n = t.length();
int m = s.length();
// dp[i][j]:T 前 i 个字符 和 S 前 j 个字符 的最长公共子序列长度。
int[][] dp = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (t.charAt(i - 1) == s.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]);
}
}
}
int ans = n - dp[n][m];
System.out.print(ans);
}
}
python
python
t = input()
s = input()
n = len(t)
m = len(s)
# dp[i][j]:T 前 i 个字符 和 S 前 j 个字符 的最长公共子序列长度。
dp = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, m + 1):
if t[i - 1] == s[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
ans = n - dp[n][m]
print(ans)
javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const input = [];
rl.on("line", (line) => {
input.push(line);
});
rl.on("close", () => {
const t = input[0];
const s = input[1];
const n = t.length;
const m = s.length;
// dp[i][j]:T 前 i 个字符 和 S 前 j 个字符 的最长公共子序列长度。
const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0));
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= m; j++) {
if (t[i - 1] === s[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]);
}
}
}
const ans = n - dp[n][m];
console.log(ans);
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
in := bufio.NewReader(os.Stdin)
var t, s string
fmt.Fscan(in, &t)
fmt.Fscan(in, &s)
n := len(t)
m := len(s)
// dp[i][j]:T 前 i 个字符 和 S 前 j 个字符 的最长公共子序列长度。
dp := make([][]int, n+1)
for i := 0; i <= n; i++ {
dp[i] = make([]int, m+1)
}
for i := 1; i <= n; i++ {
for j := 1; j <= m; j++ {
if t[i-1] == s[j-1] {
dp[i][j] = dp[i-1][j-1] + 1
} else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
}
}
}
ans := n - dp[n][m]
fmt.Print(ans)
}