美团研发岗 4月18号笔试真题 - 包包的最长公共子序列3

包包的最长公共子序列3

美团研发岗 四月十八号笔试 第三题 Py/Java/C++/Js/Go多语言题

题目内容

给定两个排列ppp 和 qqq,长度都为nnn。请你求出ppp和 qqq 之间字典序最大的最长公共子序列

【排列】:长度为 nnn 的排列是由 1,2,...,n1,2,...,n1,2,...,n 每个整数恰好出现一次组成的序列。例如:(2,3,1,5,4)(2,3,1,5,4)(2,3,1,5,4) 是一个长度为 555 的排列;而 (1,2,2)(1,2,2)(1,2,2) 和 (1,3,4)(1,3,4)(1,3,4) 都不是排列,因为前者存在重复元素,后者包含了超出范围的数。

【子序列】:在序列的顺序中删除任意个(可以为零、可以为全部)元素得到的新序列。

【公共子序列】:如果数组 aaa的一个子序列a′a'a′与数组 bbb 的一个子序列 b′b'b′ 完全相等,那么子序列 a′,b′a',b'a′,b′就是数组a,ba,ba,b 的一个公共子序列。

【字典序比较】:从数组的第一个元素开始逐个比较,直到找到第一个不同的位置,通过比较这个位置元素的大小得出数组的大小,称为字典序比较。

输入描述

每个测试文件包含多组测试数据:第一行输入一个整数 T(1≤T≤105)T(1≤T≤10^5)T(1≤T≤105),代表数据组数。

每组测试数据描述如下:

  • 第一行输入一个整数 n(2≤n≤2⋅105)n(2≤n≤2⋅10^5)n(2≤n≤2⋅105),代表排列 ppp 和qqq 的长度。
  • 第二行输入 nnn 个不同整数 p1,p2,...,pn(1≤pi≤n)p_1,p_2,...,p_n(1≤p_i≤n)p1,p2,...,pn(1≤pi≤n),代表排列 ppp 中的元素。
  • 第三行输入nnn 个不同整数 q1,q2,...,qn(1≤qi≤n)q_1,q_2,...,q_n(1≤q_i≤n)q1,q2,...,qn(1≤qi≤n),代表排列qqq 中的元素。
    除此之外,保证单个测试文件的 nnn 之和不超过2⋅1052⋅10^52⋅105。

输出描述

对于每组测试数据:先输出一行一个整数 k(1≤k≤n)k(1≤k≤n)k(1≤k≤n),代表 ppp 和 qqq 的最长公共子序列长度。

第二行输出 kkk 个不同整数 r1,r2,...,rkr_1,r_2,...,r_kr1,r2,...,rk,代表你找到的字典序最大的最长公共子序列。

样例1

输入

复制代码
3
4
1 3 2 4
3 4 1 2
5
1 2 3 4 5
5 4 3 2 1
9
9 2 6 7 3 8 1 4 5
6 2 7 9 4 8 3 5 1

输出

复制代码
2
3 4
1
5
4
6 7 8 5

题解

思路

动态规划

  1. 由于题目说明p和q都是长度 n的排列是由 1,2,...,n 每个整数恰好出现一次组成的序列可以根据这个将最长公共子序列转换为最长递增子序列问题。简单逻辑如下:
    • 将p所有元素转换为p[i]在q的索引,使用a数组保存
    • 那么a的最长递增子序列长度就是p和q最长公共子序列长度
  2. 本题需要找到字典序最大的最长公共子序列,这里需要把思路进行转变。定义f[i]表示从i开始的LIS长度, 状态转移为f[i] = 1 + max(f[j]) (j > i 且 a[j] > a[i]),同时由于本题数据量较大,可以引入线段树维护维护在当前位置右侧,满足条件的最大 LIS 长度加速状态转移。
  3. 按照2的逻辑从后往前遍历a确定每个f的值,其中最大长度L = max(f[i])
  4. 至于构造字典序最大公共子序列可以利用f[i]分层贪心构造最大值,这部分可参照下面代码实现。

C++

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

// 维护在当前位置右侧,满足条件的最大 LIS 长度
struct SegTree {
    int n;
    vector<int> tr;
    
    SegTree(int n) : n(n) {
        tr.assign(4 * n  + 4, 0);
    }
    
    void update(int p, int v, int id, int l, int r) {
        if (l == r) {
            tr[id] = max(tr[id], v);
            return;
        }
        int mid = (l + r) >> 1;
        if (p <= mid) {
            update(p, v, id * 2, l , mid);
        } else {
            update(p, v, id * 2 + 1, mid + 1, r);
        }
        tr[id] = max(tr[id * 2], tr[id * 2 + 1]);
    }
    
    void update(int p, int v) {
        update(p, v, 1, 0, n - 1);
    }
    
    int query(int L, int R, int id, int l, int r) {
        if (L > R) {
            return 0;
        }
        if (L <= l && r <= R) {
            return tr[id];
        }
        int mid = (l + r) >> 1;
        int ans = 0;
        if (L <= mid) {
            ans = max(ans, query(L, R, id * 2, l, mid));
        }
        if (R > mid) {
            ans = max(ans, query(L, R, id * 2 + 1, mid + 1, r));
        }
        return ans;
    }
    
    // 查询在[L,R]范围内最大f
    int query(int L, int R) {
        if (L > R) {
            return 0;
        }
        return query(L,R,1,0,n-1);
    }
};


int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;

    while (T--) {
        int n;
        cin >> n;
        vector<int> p(n), q(n);
        for (int i = 0; i < n; i++) {
            cin >> p[i];
        }
        for (int i = 0; i < n; i++) {
            cin >> q[i];
        }
        // 记录 i 在 q中位置 
        vector<int> pos(n + 1);
        for (int i = 0; i < n; i++) {
            pos[q[i]] = i;
        }
        // 将LCS转换为LIS
        vector<int> a(n);
        for (int i = 0; i < n; i++) {
            a[i] = pos[p[i]];
        }
        // 从i开始的LIS长度
        vector<int> f(n);
        SegTree seg(n);
        int L = 0;
        for (int i = n - 1; i >= 0; i--) {
            f[i] = seg.query(a[i] + 1, n - 1) + 1;
            seg.update(a[i], f[i]);
            L = max(L, f[i]);
        }
        vector<vector<int>> layer(L + 1);
        for (int i = 0; i < n; i++) {
            layer[f[i]].push_back(i);
        }
        
        // 每个位置按照实际值降序
        for (int len = 1; len <= L; len++) {
            sort(layer[len].begin(), layer[len].end(),[&](int x,int y){
                return p[x] > p[y];
            });
         }
         
        vector<int> ans;
        int lastPos = -1;
        int lastA = -1;
        // 贪心构造 每一层先选最长
        for (int need = L; need >= 1; need--) {
             for (int idx : layer[need]) {
                 if (idx <= lastPos) {
                     continue;
                 }
                 if (a[idx] <= lastA) {
                     continue;
                 }
                 ans.push_back(p[idx]);
                 lastPos = idx;
                 lastA = a[idx];
                 break;
             }
        }
        cout << L << endl;
        for (int i = 0; i < (int)ans.size(); i++) {
            if (i) cout << ' ';
            cout << ans[i];
        }
        cout << endl;
    }  

    return 0;
}

java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {

    // 线段树:维护区间最大 LIS
    static class SegTree {
        int n;
        int[] tr;

        SegTree(int n) {
            this.n = n;
            tr = new int[4 * n + 5];
        }

        void update(int p, int v, int id, int l, int r) {
            if (l == r) {
                tr[id] = Math.max(tr[id], v);
                return;
            }

            int mid = (l + r) >> 1;

            if (p <= mid) update(p, v, id << 1, l, mid);
            else update(p, v, id << 1 | 1, mid + 1, r);

            tr[id] = Math.max(tr[id << 1], tr[id << 1 | 1]);
        }

        void update(int p, int v) {
            update(p, v, 1, 0, n - 1);
        }

        int query(int L, int R, int id, int l, int r) {
            if (L > R) return 0;

            if (L <= l && r <= R) return tr[id];

            int mid = (l + r) >> 1;
            int ans = 0;

            if (L <= mid) ans = Math.max(ans, query(L, R, id << 1, l, mid));
            if (R > mid) ans = Math.max(ans, query(L, R, id << 1 | 1, mid + 1, r));

            return ans;
        }

        int query(int L, int R) {
            return query(L, R, 1, 0, n - 1);
        }
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        int T = Integer.parseInt(br.readLine());

        while (T-- > 0) {

            int n = Integer.parseInt(br.readLine());

            int[] p = new int[n];
            int[] q = new int[n];

            st = new StringTokenizer(br.readLine());
            for (int i = 0; i < n; i++) p[i] = Integer.parseInt(st.nextToken());

            st = new StringTokenizer(br.readLine());
            for (int i = 0; i < n; i++) q[i] = Integer.parseInt(st.nextToken());

            // 记录 i 在 q 中位置
            int[] pos = new int[n + 1];
            for (int i = 0; i < n; i++) pos[q[i]] = i;

            // 将 LCS 转换为 LIS
            int[] a = new int[n];
            for (int i = 0; i < n; i++) a[i] = pos[p[i]];

            // 从 i 开始的 LIS 长度
            int[] f = new int[n];

            SegTree seg = new SegTree(n);

            int L = 0;

            for (int i = n - 1; i >= 0; i--) {
                f[i] = seg.query(a[i] + 1, n - 1) + 1;
                seg.update(a[i], f[i]);
                L = Math.max(L, f[i]);
            }

            // 分层
            List<List<Integer>> layer = new ArrayList<>();
            for (int i = 0; i <= L; i++) layer.add(new ArrayList<>());

            for (int i = 0; i < n; i++) {
                layer.get(f[i]).add(i);
            }

            // 每个位置按照实际值降序
            for (int len = 1; len <= L; len++) {
                final int[] pArr = p;
                layer.get(len).sort((x, y) -> pArr[y] - pArr[x]);
            }

            // 贪心构造
            List<Integer> ans = new ArrayList<>();

            int lastPos = -1;
            int lastA = -1;

            for (int need = L; need >= 1; need--) {

                for (int idx : layer.get(need)) {

                    if (idx <= lastPos) continue;   // p中顺序约束
                    if (a[idx] <= lastA) continue;   // q中顺序约束

                    ans.add(p[idx]);
                    lastPos = idx;
                    lastA = a[idx];
                    break;
                }
            }

            System.out.println(L);

            for (int i = 0; i < ans.size(); i++) {
                if (i > 0) System.out.print(" ");
                System.out.print(ans.get(i));
            }
            System.out.println();
        }
    }
}

python

python 复制代码
import sys
input = sys.stdin.readline

# 线段树:维护区间最大 LIS
class SegTree:
    def __init__(self, n):
        self.n = n
        self.tr = [0] * (4 * n)

    def update(self, p, v, id, l, r):
        if l == r:
            self.tr[id] = max(self.tr[id], v)
            return

        mid = (l + r) // 2

        if p <= mid:
            self.update(p, v, id * 2, l, mid)
        else:
            self.update(p, v, id * 2 + 1, mid + 1, r)

        self.tr[id] = max(self.tr[id * 2], self.tr[id * 2 + 1])

    def query(self, L, R, id, l, r):
        if L > R:
            return 0

        if L <= l and r <= R:
            return self.tr[id]

        mid = (l + r) // 2
        ans = 0

        if L <= mid:
            ans = max(ans, self.query(L, R, id * 2, l, mid))
        if R > mid:
            ans = max(ans, self.query(L, R, id * 2 + 1, mid + 1, r))

        return ans

    def query_range(self, L, R):
        return self.query(L, R, 1, 0, self.n - 1)

    def update_root(self, p, v):
        self.update(p, v, 1, 0, self.n - 1)


T = int(input())

while T:
    T -= 1

    n = int(input())

    p = list(map(int, input().split()))
    q = list(map(int, input().split()))

    # 记录 i 在 q 中位置
    pos = [0] * (n + 1)
    for i in range(n):
        pos[q[i]] = i

    # 将 LCS 转换为 LIS
    a = [pos[x] for x in p]

    # 从 i 开始的 LIS 长度
    f = [0] * n

    seg = SegTree(n)

    L = 0

    for i in range(n - 1, -1, -1):
        f[i] = seg.query_range(a[i] + 1, n - 1) + 1
        seg.update_root(a[i], f[i])
        L = max(L, f[i])

    # 分层
    layer = [[] for _ in range(L + 1)]

    for i in range(n):
        layer[f[i]].append(i)

    # 每个位置按照实际值降序
    for len_ in range(1, L + 1):
        layer[len_].sort(key=lambda x: -p[x])

    # 贪心构造
    ans = []

    lastPos = -1
    lastA = -1

    for need in range(L, 0, -1):
        for idx in layer[need]:

            if idx <= lastPos:
                continue
            if a[idx] <= lastA:
                continue

            ans.append(p[idx])
            lastPos = idx
            lastA = a[idx]
            break

    print(L)
    print(*ans)

javascript

js 复制代码
// ACM 标准输入:readline版本
const readline = require("readline");

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

let input = [];
rl.on("line", line => {
    input.push(line.trim());
});

rl.on("close", () => {
    let idx = 0;

    let T = Number(input[idx++]);
    let out = [];

    class SegTree {
        constructor(n) {
            this.n = n;
            this.tr = new Array(4 * n).fill(0);
        }

        // 线段树:维护区间最大 LIS
        update(p, v, id, l, r) {
            if (l === r) {
                this.tr[id] = Math.max(this.tr[id], v);
                return;
            }

            let mid = (l + r) >> 1;

            if (p <= mid) this.update(p, v, id * 2, l, mid);
            else this.update(p, v, id * 2 + 1, mid + 1, r);

            this.tr[id] = Math.max(this.tr[id * 2], this.tr[id * 2 + 1]);
        }

        query(L, R, id, l, r) {
            if (L > R) return 0;

            if (L <= l && r <= R) return this.tr[id];

            let mid = (l + r) >> 1;
            let ans = 0;

            if (L <= mid) ans = Math.max(ans, this.query(L, R, id * 2, l, mid));
            if (R > mid) ans = Math.max(ans, this.query(L, R, id * 2 + 1, mid + 1, r));

            return ans;
        }

        queryRange(L, R) {
            return this.query(L, R, 1, 0, this.n - 1);
        }

        updateRoot(p, v) {
            this.update(p, v, 1, 0, this.n - 1);
        }
    }

    while (T--) {

        let n = Number(input[idx++]);

        let p = input[idx++].split(" ").map(Number);
        let q = input[idx++].split(" ").map(Number);

        // 记录 i 在 q 中位置
        let pos = new Array(n + 1).fill(0);
        for (let i = 0; i < n; i++) {
            pos[q[i]] = i;
        }

        // 将 LCS 转换为 LIS
        let a = p.map(x => pos[x]);

        // 从 i 开始的 LIS 长度
        let f = new Array(n).fill(0);

        let seg = new SegTree(n);

        let L = 0;

        for (let i = n - 1; i >= 0; i--) {
            f[i] = seg.queryRange(a[i] + 1, n - 1) + 1;
            seg.updateRoot(a[i], f[i]);
            L = Math.max(L, f[i]);
        }

        // 分层
        let layer = Array.from({ length: L + 1 }, () => []);

        for (let i = 0; i < n; i++) {
            layer[f[i]].push(i);
        }

        // 每层按 p 值降序
        for (let i = 1; i <= L; i++) {
            layer[i].sort((x, y) => p[y] - p[x]);
        }

        // 贪心构造
        let ans = [];

        let lastPos = -1;
        let lastA = -1;

        for (let need = L; need >= 1; need--) {

            for (let idx of layer[need]) {

                if (idx <= lastPos) continue;
                if (a[idx] <= lastA) continue;

                ans.push(p[idx]);
                lastPos = idx;
                lastA = a[idx];
                break;
            }
        }

        out.push(L);
        out.push(ans.join(" "));
    }

    console.log(out.join("\n"));
});

Go

go 复制代码
package main

import (
    "bufio"
    "fmt"
    "os"
    "sort"
)

type SegTree struct {
    n  int
    tr []int
}

// 线段树:维护区间最大 LIS
func NewSegTree(n int) *SegTree {
    return &SegTree{
        n:  n,
        tr: make([]int, 4*n),
    }
}

func (s *SegTree) update(p, v, id, l, r int) {
    if l == r {
        if v > s.tr[id] {
            s.tr[id] = v
        }
        return
    }

    mid := (l + r) >> 1

    if p <= mid {
        s.update(p, v, id*2, l, mid)
    } else {
        s.update(p, v, id*2+1, mid+1, r)
    }

    if s.tr[id*2] > s.tr[id*2+1] {
        s.tr[id] = s.tr[id*2]
    } else {
        s.tr[id] = s.tr[id*2+1]
    }
}

func (s *SegTree) query(L, R, id, l, r int) int {
    if L > R {
        return 0
    }
    if L <= l && r <= R {
        return s.tr[id]
    }

    mid := (l + r) >> 1
    ans := 0

    if L <= mid {
        v := s.query(L, R, id*2, l, mid)
        if v > ans {
            ans = v
        }
    }

    if R > mid {
        v := s.query(L, R, id*2+1, mid+1, r)
        if v > ans {
            ans = v
        }
    }

    return ans
}

func main() {
    in := bufio.NewScanner(os.Stdin)
    in.Split(bufio.ScanWords)

    read := func() int {
        in.Scan()
        var x int
        fmt.Sscan(in.Text(), &x)
        return x
    }

    T := read()

    for T > 0 {
        T--

        n := read()

        p := make([]int, n)
        q := make([]int, n)

        for i := 0; i < n; i++ {
            p[i] = read()
        }
        for i := 0; i < n; i++ {
            q[i] = read()
        }

        // 记录 i 在 q 中位置
        pos := make([]int, n+1)
        for i := 0; i < n; i++ {
            pos[q[i]] = i
        }

        // 将 LCS 转换为 LIS
        a := make([]int, n)
        for i := 0; i < n; i++ {
            a[i] = pos[p[i]]
        }

        // 从 i 开始的 LIS 长度
        f := make([]int, n)

        seg := NewSegTree(n)

        L := 0

        for i := n - 1; i >= 0; i-- {
            f[i] = seg.query(a[i]+1, n-1, 1, 0, n-1) + 1
            seg.update(a[i], f[i], 1, 0, n-1)
            if f[i] > L {
                L = f[i]
            }
        }

        // 分层
        layer := make([][]int, L+1)
        for i := 0; i <= L; i++ {
            layer[i] = []int{}
        }

        for i := 0; i < n; i++ {
            layer[f[i]] = append(layer[f[i]], i)
        }

        // 每个位置按照实际值降序
        for len := 1; len <= L; len++ {
            sort.Slice(layer[len], func(i, j int) bool {
                return p[layer[len][i]] > p[layer[len][j]]
            })
        }

        // 贪心构造
        ans := []int{}
        lastPos := -1
        lastA := -1

        for need := L; need >= 1; need-- {
            for _, idx := range layer[need] {

                if idx <= lastPos {
                    continue
                }
                if a[idx] <= lastA {
                    continue
                }

                ans = append(ans, p[idx])
                lastPos = idx
                lastA = a[idx]
                break
            }
        }

        fmt.Println(L)
        for i, v := range ans {
            if i > 0 {
                fmt.Print(" ")
            }
            fmt.Print(v)
        }
        fmt.Println()
    }
}
相关推荐
阿里matlab建模师1 小时前
基于matlab时域频域处理的语音信号变声处理系统设计与算法原理(论文+程序源码+GUI图形用户界面)——变声算法
算法·matlab·语音识别
IMPYLH2 小时前
HTML 的 <abbr> 元素
前端·算法·html
leo__5202 小时前
小波特征与模糊支持向量机(FSVM)的脑电信号分类方法
算法·支持向量机·分类
wabs6662 小时前
关于动态规划【纯粹的0-1背包需要思考的问题】
算法·动态规划
小小编程路2 小时前
字符串转数字时,可能会遇到哪些问题?
java·开发语言·算法
rit84324992 小时前
MATLAB近红外光谱预处理:平滑与求导(MSV方法)
数据结构·算法·matlab
蚂蚁数据AntData2 小时前
从ChatBI到业务记忆:重新定义数据智能的生产力边界
大数据·网络·数据库·人工智能·算法
_日拱一卒2 小时前
LeetCode:22括号生成
算法·leetcode·职场和发展
cfm_29142 小时前
JVM垃圾收集算法与收集器深度解析
jvm·测试工具·算法·性能优化