1 条题解

  • 0
    @ 2026-9-24 0:04:47

    论文题,同时是构造好题,放模拟赛被大神们秒掉了,我已吓哭。

    题目的限制为公共子序列的每个空隙里两个串不能有公共字符,为方便简称这个子序列为 MCS,MCS 的每个位置选择为匹配。

    但是并不能根据这个直接进行判定 MCS 和构造,最大的问题在于匹配位置可能不唯一。考虑从左到右贪心匹配,当前字符可以匹配就匹配上,否则就返回。这个做法就会错掉,一个 hack:CAGA CGA。如果直接贪心匹配,那么会匹配成 CA。

    陷入僵局,先思考判定:判定的关键在于空隙中不能有公共字符,贪心的想,为达成这个限制,对于每组空隙,一定会选择使这组空隙最大的匹配方案来判定。

    受到判定的启发,我们考虑魔改我们的贪心匹配做法,对于剩余没有匹配的情况,选择当前字符一定是优的,但是选择的位置不一定优。我们将当前字符加入答案,并删去距离当前结尾最近的同字符位置即可(只有最后面位置与结尾还存在匹配的情况该字符才不优,但是如果出现这种情况,贪心判定一定可以继续,矛盾)。

    这样就可以编出做法了,保留贪心匹配的过程,用一个栈维护匹配,如果已经到达匹配极限就删点更新边界并加入答案,预处理一下 prex,ipre_{x,i} 表示位置 ii 前第一个字符 xx 的位置和 nxtx,inxt_{x,i} 表示位置 ii 后第一个字符 xx 的位置即可快速匹配,复杂度 O(n)O(n)。

    判定是好判的,处理满足当前匹配方案的最小前缀和最大后缀,然后枚举中间的空隙,判定最小前缀和最大后缀之间是否有公共字符即可,与字符集大小无关,均为 O(n)O(n)。

    原论文中处理了字符集可能很大的问题,可以做到 O(nlog⁡log⁡n)O(n\log \log n) 复杂度,其实就是用 y-fast trie 维护寻找前驱后继的过程,地址

    :::info[code]

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6 + 10, M = 4;
    int n, m, pres[M][N], nxts[M][N], pret[M][N], nxtt[M][N], top = 0;
    char s[N], t[N];
    struct GG
    {
        int x, y, c;
    };
    vector<GG> stk;
    vector<int> ans;
    int get(char a)
    {
        if (a == 'A')
            return 0;
        else if (a == 'C')
            return 1;
        else if (a == 'G')
            return 2;
        return 3;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0), cout.tie(0);
        cin >> (s + 1) >> (t + 1);
        n = strlen(s + 1), m = strlen(t + 1);
        for (int i = 2; i <= n + 1; i++)
        {
            for (int j = 0; j < 4; j++)
                pres[j][i] = pres[j][i - 1];
            pres[get(s[i - 1])][i] = i - 1;
        }
        for (int i = 2; i <= m + 1; i++)
        {
            for (int j = 0; j < 4; j++)
                pret[j][i] = pret[j][i - 1];
            pret[get(t[i - 1])][i] = i - 1;
        }
        for (int i = n - 1; i >= 0; i--)
        {
            for (int j = 0; j < 4; j++)
                nxts[j][i] = nxts[j][i + 1];
            nxts[get(s[i + 1])][i] = i + 1;
        }
        for (int i = m - 1; i >= 0; i--)
        {
            for (int j = 0; j < 4; j++)
                nxtt[j][i] = nxtt[j][i + 1];
            nxtt[get(t[i + 1])][i] = i + 1;
        }
        int id1 = 0, id2 = 0;
        n++, m++;
        while (1)
        {
            while (1)
            {
                int id = 0, mx = 1e9;
                for (int j = 0; j < 4; j++)
                {
                    if (nxts[j][id1] && nxtt[j][id2] && nxts[j][id1] < n && nxtt[j][id2] < m)
                    {
                        if (nxts[j][id1] < mx)
                        {
                            mx = nxts[j][id1];
                            id = j;
                        }
                    }
                }
                if (mx == 1e9)
                    break;
                id1 = nxts[id][id1], id2 = nxtt[id][id2];
                stk.push_back({id1, id2, id});
            }
            if (stk.size())
            {
                auto t = stk.back();
                stk.pop_back();
                n = pres[t.c][n], m = pret[t.c][m];
                ans.push_back(t.c);
                if (stk.size())
                    id1 = stk.back().x, id2 = stk.back().y;
                else
                    id1 = 0, id2 = 0;
            }
            else
                break;
        }
        reverse(ans.begin(), ans.end());
        for (auto t : ans)
        {
            if (t == 0)
                cout << "A";
            else if (t == 1)
                cout << "C";
            else if (t == 2)
                cout << "G";
            else
                cout << "T";
        }
    }
    

    :::

    • 1

    信息

    ID
    6661
    时间
    3000ms
    内存
    612MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者