拼多多校招笔试真题 10.18 - 甲乙指示灯链(C++/Py/Java /Js/Go)

甲乙指示灯链

拼多多 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,没有任何一只灯泡能同时留在两边。六只都要摘掉。

题解

思路

解题思路: 动态规划

  1. 这题如果没有限制紧挨着的两只灯泡不能同色就是最长公共子序列问题。假设最长公共子序列长度为L,那么一共需要摘掉的长度为p.size() + q.size() - 2 * L
  2. 考虑到相邻不能同色的限制,定义状态dp[i][j][c]表示在 q 的前 i 个字符和 p 的前 j 个字符中,选取一个满足相邻字符不同、且以字符 c 结尾的公共子序列时,能够达到的最大长度。
  3. 状态转移,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)
  4. 最后最长公共子序列以及满足相邻不重复的长度为L = max(dp[n][m][0],dp[n][m][1],dp[n][m][2])。
  5. 算法时间复杂度为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();
});
相关推荐
andrsted1 小时前
练题簿在线免费刷题:让整理过的资料,变成能反复练习的题库
学习·微信小程序·小程序·学习方法·刷题·1024程序员节
2601_952047791 小时前
分步式调出单同步:金蝶云星空到MySQL的实战拆解
1024程序员节
伟大的前程1 小时前
8051 FLASH架构
1024程序员节
小小张自由—>张有博2 小时前
blog-edge-ops-platform
1024程序员节
AI人工智能+电脑小能手2 小时前
一次请求打几个下游,这个Java并行任务工具类6个方法按序聚合
java·线程池·并发编程·1024程序员节·批量处理·并行任务
CarIise2 小时前
JavaWeb 文件上传的四种实现方案
1024程序员节
长欢喜一场2 小时前
豆包:字节跳动AI助手的全方位透视
人工智能·搜索引擎·1024程序员节
4SAPI2 小时前
搭建 AI 数据分析工具大模型 API 选型指南
数据库·人工智能·架构·php·1024程序员节
KingOTC2 小时前
国产模块测评:基于国产芯片的实际表现
rfid·1024程序员节