甲乙指示灯链
拼多多 10月18号 笔试真题 第三题
拼多多真题目录点击查看: 拼多多笔试真题题库目录|笔试题库 + 算法考点详解
题目内容
车间并列挂着甲、乙两条指示灯链。每条链由若干只灯泡串成,每只灯泡只亮红、绿、蓝中的一种,分别记作 R \mathrm{R} R、 G \mathrm{G} G、 B \mathrm{B} B。
可以摘掉任意灯泡。摘掉之后,它左右两边的灯泡会靠拢接上,原有的先后次序不变。收工时两条链留下的颜色序列必须一模一样,并且序列里紧挨着的两只灯泡不能同色。
请算出最少一共要摘掉多少只灯泡。若两边都摘空,两条空链视为相同,也视为满足相邻不同色。
输入描述
仅一行,两个由大写字母组成的字符串 q q q 和 p p p,中间用一个空格分开。 q q q 是乙链, p p p 是甲链。
两个字符串只含 R \mathrm{R} R、 G \mathrm{G} G、 B \mathrm{B} B。
1 ≤ ∣ q ∣ , ∣ p ∣ ≤ 2000 1 \le |q|,|p| \le 2000 1≤∣q∣,∣p∣≤2000
输出描述
只写出一个整数,表示最少摘掉的灯泡只数。
样例1
输入
RBRG RGBG
输出
2
说明
甲链 R G B G \mathrm{RGBG} RGBG 摘掉第二只,剩下 R B G \mathrm{RBG} RBG。乙链 R B R G \mathrm{RBRG} RBRG 摘掉第三只,剩下 R B G \mathrm{RBG} RBG。 R B G \mathrm{RBG} RBG 中相邻颜色都不一样。两边各摘一只,总数是 2 2 2。
样例2
输入
GB GB
输出
0
说明
两条链本来就是 G B \mathrm{GB} GB,彼此相同,相邻颜色也不同,不用摘。
样例3
输入
BBB GGG
输出
6
说明
乙链全是 B \mathrm{B} B,甲链全是 G \mathrm{G} G,没有任何一只灯泡能同时留在两边。六只都要摘掉。
题解
思路
解题思路: 动态规划
- 这题如果没有限制
紧挨着的两只灯泡不能同色就是最长公共子序列问题。假设最长公共子序列长度为L,那么一共需要摘掉的长度为p.size() + q.size() - 2 * L - 考虑到相邻不能同色的限制,定义状态
dp[i][j][c]表示在 q 的前 i 个字符和 p 的前 j 个字符中,选取一个满足相邻字符不同、且以字符 c 结尾的公共子序列时,能够达到的最大长度。 - 状态转移,
q[i]和p[j]q[i] != p[j]限制下,不能同时选取两个字符,只能从前面继承,dp[i][j][c] = max(dp[i - 1][j][c], dp[i][j - 1][c])q[i] == p[j]限制下,可以同时选取两个字符,但是不允许前一个字符为c,选择其它两个较大的best = max({dp[i - 1][j - 1][(c + 1) % 3],dp[i - 1][j - 1][(c + 2) % 3]}),从而dp[i][j][c] = max(dp[i][j][c], best + 1)
- 最后最长公共子序列以及满足相邻不重复的长度为
L = max(dp[n][m][0],dp[n][m][1],dp[n][m][2])。 - 算法时间复杂度为
O(nm)
C++
cpp
#include <bits/stdc++.h>
using namespace std;
int getId(char c) {
if (c == 'R'){
return 0;
} else if (c == 'G') {
return 1;
}
return 2;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string q, p;
cin >> p >> q;
int n = q.size(), m = p.size();
// 表示在 q 的前 i 个字符和 p 的前 j 个字符中,选取一个满足相邻字符不同、且以字符 c 结尾的公共子序列时,能够达到的最大长度。
vector<vector<vector<int>>> dp(n + 1, vector<vector<int>>(m + 1, vector<int>(3, 0)));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int c = 0; c <3; c++) {
dp[i][j][c] = max(dp[i - 1][j][c], dp[i][j - 1][c]);
}
if (q[i - 1] == p[j -1]) {
int c = getId(q[i - 1]);
// 取另外两种较大值
int best = max({
dp[i - 1][j - 1][(c + 1) % 3],
dp[i - 1][j - 1][(c + 2) % 3]
});
dp[i][j][c] = max(dp[i][j][c], best + 1);
}
}
}
int L = max({
dp[n][m][0],
dp[n][m][1],
dp[n][m][2]
});
cout << n + m - 2 * L << '\n';
return 0;
}
java
java
import java.util.*;
public class Main {
static int getId(char c) {
if (c == 'R') {
return 0;
} else if (c == 'G') {
return 1;
}
return 2;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String p = sc.next();
String q = sc.next();
int n = q.length(), m = p.length();
// 表示在 q 的前 i 个字符和 p 的前 j 个字符中,选取一个满足相邻字符不同、且以字符 c 结尾的公共子序列时,能够达到的最大长度。
int[][][] dp = new int[n + 1][m + 1][3];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int c = 0; c < 3; c++) {
dp[i][j][c] = Math.max(dp[i - 1][j][c], dp[i][j - 1][c]);
}
if (q.charAt(i - 1) == p.charAt(j - 1)) {
int c = getId(q.charAt(i - 1));
// 取另外两种较大值
int best = Math.max(
dp[i - 1][j - 1][(c + 1) % 3],
dp[i - 1][j - 1][(c + 2) % 3]
);
dp[i][j][c] = Math.max(dp[i][j][c], best + 1);
}
}
}
int L = Math.max(
dp[n][m][0],
Math.max(dp[n][m][1], dp[n][m][2])
);
System.out.println(n + m - 2 * L);
sc.close();
}
}
python
python
def get_id(c):
if c == 'R':
return 0
elif c == 'G':
return 1
return 2
p, q = input().split()
n, m = len(q), len(p)
# 表示在 q 的前 i 个字符和 p 的前 j 个字符中,选取一个满足相邻字符不同、且以字符 c 结尾的公共子序列时,能够达到的最大长度。
dp = [[[0] * 3 for _ in range(m + 1)] for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, m + 1):
for c in range(3):
dp[i][j][c] = max(dp[i - 1][j][c], dp[i][j - 1][c])
if q[i - 1] == p[j - 1]:
c = get_id(q[i - 1])
# 取另外两种较大值
best = max(
dp[i - 1][j - 1][(c + 1) % 3],
dp[i - 1][j - 1][(c + 2) % 3]
)
dp[i][j][c] = max(dp[i][j][c], best + 1)
L = max(dp[n][m][0], dp[n][m][1], dp[n][m][2])
print(n + m - 2 * L)
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func getId(c byte) int {
if c == 'R' {
return 0
} else if c == 'G' {
return 1
}
return 2
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
reader := bufio.NewReader(os.Stdin)
var p, q string
fmt.Fscan(reader, &p, &q)
n, m := len(q), len(p)
// 表示在 q 的前 i 个字符和 p 的前 j 个字符中,选取一个满足相邻字符不同、且以字符 c 结尾的公共子序列时,能够达到的最大长度。
dp := make([][][]int, n+1)
for i := 0; i <= n; i++ {
dp[i] = make([][]int, m+1)
for j := 0; j <= m; j++ {
dp[i][j] = make([]int, 3)
}
}
for i := 1; i <= n; i++ {
for j := 1; j <= m; j++ {
for c := 0; c < 3; c++ {
dp[i][j][c] = max(
dp[i-1][j][c],
dp[i][j-1][c],
)
}
if q[i-1] == p[j-1] {
c := getId(q[i-1])
// 取另外两种较大值
best := max(
dp[i-1][j-1][(c+1)%3],
dp[i-1][j-1][(c+2)%3],
)
dp[i][j][c] = max(dp[i][j][c], best+1)
}
}
}
L := max(
dp[n][m][0],
max(dp[n][m][1], dp[n][m][2]),
)
fmt.Println(n + m - 2*L)
}
javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', (line) => {
const [p, q] = line.trim().split(/\s+/);
const n = q.length;
const m = p.length;
function getId(c) {
if (c === 'R') {
return 0;
} else if (c === 'G') {
return 1;
}
return 2;
}
// 表示在 q 的前 i 个字符和 p 的前 j 个字符中,选取一个满足相邻字符不同、且以字符 c 结尾的公共子序列时,能够达到的最大长度。
const dp = Array.from({ length: n + 1 }, () =>
Array.from({ length: m + 1 }, () => [0, 0, 0])
);
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= m; j++) {
for (let c = 0; c < 3; c++) {
dp[i][j][c] = Math.max(
dp[i - 1][j][c],
dp[i][j - 1][c]
);
}
if (q[i - 1] === p[j - 1]) {
const c = getId(q[i - 1]);
// 取另外两种较大值
const best = Math.max(
dp[i - 1][j - 1][(c + 1) % 3],
dp[i - 1][j - 1][(c + 2) % 3]
);
dp[i][j][c] = Math.max(dp[i][j][c], best + 1);
}
}
}
const L = Math.max(
dp[n][m][0],
dp[n][m][1],
dp[n][m][2]
);
console.log(n + m - 2 * L);
rl.close();
});