首页 > 其他分享 >[CF83E] Two Subsequences 题解

[CF83E] Two Subsequences 题解

时间:2023-12-06 23:55:24浏览次数:32  
标签:状态 CF83E 题解 overlap Subsequences max 序列 转移 sim

[CF83E] Two Subsequences 题解

思路

定义 \(overlap(a, b)\) 为字符串 \(a\) 的后缀与 \(b\) 的前缀的最大相等的长度,有 \(|f(a, b)| = |a| + |b| - overlap(a, b)\),下文称匹配为相邻两串的 \(overlap\)。

观察到每次操作之后,一定有一个序列是以 \(a_i\) 为结尾的。

所以根据这个性质设计状态:\(f_{i ,j}\) 表示考虑到 \(a_i\),另一个序列以 \(a_j\) 结尾的最少长度。

但是这个状态设计并不好,因为没法进一步优化,发现 \(|a_i|\le 20\),所以尝试把另一个序列的结尾状压进状态表示,另外为了方便,可以做一步转化(不做也可以):最后序列的最短长度就等于总长度减去最多能匹配的长度。

所以可以列出这个状态表示:\(f_{i, s}\) 表示考虑到 \(a_i\),另一个序列结尾为 \(s\) 的最大匹配数量。

推出状态转移方程为:(为了方便书写,这里省略和状态原值取 \(\max\) 的步骤)

\[\begin{aligned} f_{i, s}\gets f_{i - 1, s} + overlap(a_{i - 1}, a_i)\\ f_{i, a_{i - 1}}\gets f_{i - 1, s} + overlap(s, a_i) \end{aligned} \]

  • 第一个转移代表把 \(a_{i - 1}\) 和 \(a_{i}\) 放到同一个序列里面;

  • 第二个转移代表 \(a_{i - 1}\) 和 \(a_i\) 不在同一个序列里面,这种情况下要枚举另一个序列结尾本来是什么来转移。

这样是 \(O(2^{|a|}n)\) 的,会超时,考虑优化:

首先发现第一个转移是对所有的 \(f_{i, s}\) 都加上一个值之后取 \(\max\),因为是对所有状态的操作,所以每次加上的这个值可以使用一个标记 \(tag\) 维护。

那么瓶颈就到了第二个转移上面,发现第二个转移的特点是只有一个状态发生了改变,并且每次加上的值都介于 \(0\sim |a|\) 之间,非常小,考虑枚举加上的值,也就是上一个串 \(s\) 的后缀和 \(a_i\) 前缀匹配数。

这样就可以把第二个转移写成:

\[f_{i, a_{i - 1}} \gets \max_{k = 0}^{|a|}(\max_{s_{|a| - k + 1\sim |a|} = a_{i_{1\sim k}}} (f_{i - 1, s}) + k) \]

进一步发现第二个 \(\max\) 可以预处理出来,具体而言,定义 \(g^i_{k, x}\) 表示 \(\max_{s_{|a| - k + 1\sim |a|} = x} (f_{i - 1, s})\)。

把 \(g\) 代入原式:

\[f_{i, a_{i - 1}} \gets \max_{k = 0}^{|a|}(g^i_{k, a_{i_{1\sim k}}} + k) \]

这样如果得到了 \(g\) 数组,我们就可以实现对 \(f\) 的 \(O(|a|)\) 转移。

如何得到 \(g\) 呢,我们发现对于 \(g^i\) 而言它的大部分转移都和 \(g^{i - 1}\) 相似,只会相差 \(\Delta tag\),而唯一一个会改变的是第二维为 \(a_{i - 1}\) 的状态,这个每次更新完 \(f\) 之后对 \(g\) 进行更新即可。

之后进行滚动数组优化即可通过此题。

实现

注意一些细节,所有的 \(f\) 状态是默认会加上 \(tag\) 的,所以转移的时候要减掉 \(\Delta tag\) 来转移。

时间复杂度:\(O(n|a|\sim n|a|^2)\),这个波浪号取决于你怎么实现 \(overlap\),时间允许当然暴力好写,追求极致可以写 Exkmp。

// Problem: Two Subsequences
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-12-06 22:07:10

#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 2e5 + 10;

string a[N];
int n, m, f[(1 << 20) + 5], g[22][(1 << 20) + 5], tag;
int overlap(string a, string b) {
    for(int i = m; ~i; i --) {
        bool flg = 1;
        for(int j = 0; j < i && flg; j ++)
            if(a[m - i + j] != b[j]) flg = 0;
        if(flg) return i;
    }
    return 0;
}
int change(string x) {
    int t = 0;
    for(int i = 0; i < m; i ++)
        t = t * 2 + (x[i] - '0');
    return t;
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    m = a[1].size();
    memset(f, -0x3f, sizeof f), memset(g, -0x3f, sizeof g);
    for(int i = 2, delta = 0; i <= n; i ++) {
        delta = overlap(a[i - 1], a[i]);
        int now = change(a[i - 1]);
        tag += delta;
        f[now] = max(-delta, f[now]); // 这个转移来自于那些本来第二个序列里面没有放东西的
        for(int j = 0, t = 0; j <= m; j ++) {
            f[now] = max(f[now], g[j][t] + j - delta);
            if(j < m) t = t * 2 + (a[i][j] - '0');
        }
        for(int j = 0, t = 0; j <= m; j ++) {
            g[j][t] = max(g[j][t], f[now]);
            if(j < m) t += (1 << j) * (a[i - 1][m - j - 1] - '0');
        }
    }
    int ans = tag;
    for(int i = 0; i < (1 << m); i ++)
        ans = max(ans, f[i] + tag);
    cout << n * m - ans << '\n';
    return 0;
}

标签:状态,CF83E,题解,overlap,Subsequences,max,序列,转移,sim
From: https://www.cnblogs.com/MoyouSayuki/p/17880813.html

相关文章

  • CF1900B题解
    原题思路略微思考不难得到,三个数字的数量之差的奇偶性是不会变的。因为一个数的数量减少了$1$,另一个数无论是增加$1$或是减少$1$,两者的差要么不变,要么增加/减少$2$,对奇偶性无影响。同时,如果另外两个数的数量变为$0$,它们数目的差一定是$0$。那么,我们只需要判断另外两个......
  • 【题解】CodeForces 686E Optimal Point
    传送门:https://codeforces.com/contest/686/problem/E前言:本题解来源于作者某天晚上和一位朋友的发电内容(没错,这个作者直接把自己和朋友发电时发的话用markdown排了一下,传上来了),当时本来就比较口语化,加上作者的做法又实在太过离谱,因此可能语言表述不够清晰,对此深感抱歉qwq;离......
  • 【题解】LibreOJ #3051「十二省联考 2019」皮配
    传送门:https://loj.ac/p/3051  首先,对于这样“少部分个体有特殊要求”的题目,我们先考虑,如果没有任何个体有特殊要求怎么做,然后再考虑怎么加上特殊要求;对于这道题,如果$k=0$,即没有学校有不喜欢的导师,那么,设总人数为$al$,城市$i$的人数和为$cit_i$、选择的阵营为$zy_i=0/......
  • UVA753 A Plug for UNIX 题解
    LinkUVA753APlugforUNIXQuestion有\(n\)个插座,\(m\)个设备和\(k\)种转换器,每种转换器有无限多个。转换器可以插着转换器用,每个插座或插头的类型可能不同,求最少剩多少个不匹配的设备Sulotion先考虑转换器连用的情况,用边表\(G[x][y]\)表示\(x\)类型可以转换成\(y......
  • UVA1395 Slim Span 题解
    LinkUVA1395SlimSpanQuestion求所有生成树中最大边权与最小边权差最小的,输出他们的差值Solution因为\(n\le100\)非常小,先把边从小到大排序,那么生成树的边肯定是排序后上的边连续的一块所以,可以枚举连续一块的起点\(L\),\(R\)从\(L\)往后推,判断全图连通性,如果已经完......
  • CF1809D Binary String Sorting 题解
    题意:思路:贪心:单调不降的$01$字符串,一定是一串连续的$0$再加上一串连续的$1$。由于每次操作的代价很大,所以需要在操作次数尽可能少的情况下,尽可能多地使用交换操作。由于$1$次交换操作,只能减少$1$个逆序对,当存在多个逆序对时,优先通过删除操作减少逆序对的......
  • ucup hefei 题解
    比赛链接B很有意思的题首先题目的要求为可以拆分成\(2\)个不相交的不降子序列根据\(dilworth\)定理,最小链覆盖\(=\)最长反链,所以要求最长反链\(\le2\)即原序列的逆序列的最长不降子序列长度\(\le2\)不难得到一个\(dp\)做法为:令\(f_{i,j}\)为到数\(i\),最长上......
  • python HTML文件标题解析问题的挑战
    引言在网络爬虫中,HTML文件标题解析扮演着至关重要的角色。正确地解析HTML文件标题可以帮助爬虫准确地获取所需信息,但是在实际操作中,我们常常会面临一些挑战和问题。本文将探讨在Scrapy中解析HTML文件标题时可能遇到的问题,并提供解决方案。问题背景在解析HTML文件标题的过程中,......
  • Error: error:0308010C:digital envelope routines::unsupported 【问题解决】【转载
    原文链接:  https://www.cnblogs.com/jaxu/p/17171211.html今天早上打开电脑,更新了日常工作的github仓库,然后就是习惯性地执行了"npminstall",发现报了下面这个错误:Error:error:0308010C:digitalenveloperoutines::unsupported顺便看了一下错误堆栈,发现是一个Node......
  • 线性代数题解
    前言写完了这道题我好想刚明白一点最小割???UU好闪,拜谢UU。题解首先,我们可以发现若第\(i\)行的\(B\)没选,那么第\(i\)列的\(B\)也不选,所以此时对于行和列是等价的。若\(A_i\)是\(0\),则会减少贡献\(\sum_{j}B_{i,j}\)。否则会减少贡献\(C_i\)。当\(A_i\)是\(0\)......