abc343G 题解

这篇具有很好参考价值的文章主要介绍了abc343G 题解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

题意

给你 \(N\) 个由小写字母组成的字符串 \(S_1, S_2, \ldots, S_N\),找出一个母串使得它包含所有这些字符串作为它的子串,最小化该母串的长度并输出。

\(1 \leq N \leq 20\)\(\sum |S_i| \leq 2 \times 10 ^ 5\)

(没错洛谷翻译就是我写的)

思路

首先如果有一个字符串被另一个字符串完全包括,那么直接把被包括的字符串删了显然是不影响答案的。

对于剩下的字符串,直接把所有字符串拼接在一起形成母串肯定可行,但假如我们有两个字符串,前一个字符串的后缀和后一个的字符串前缀有一段匹配,那么将后一个字符串的这段前缀删去再加入显然也是合法的母串,所以我们可以贪心删最长匹配。

abc343G 题解

abc343G 题解

我们设 \(l_i\) 为第 \(i\)\(S_i\) 的长度,\(d(i, j)\) 为第 \(j\) 个字符串拼接在第 \(i\) 个字符串后面时最少需要额外追加的字符数量,按照刚才的思路 \(d(i, j)\) 就等于 \(l_j\) 减去 \(S_i\) 的后缀与 \(S_j\) 的前缀的最长匹配。那么该如何计算这个最长匹配呢?当然可以使用 exkmp。对于每对 \((i, j)\),因为最长匹配长度 \(\leq \min(l_i, l_j)\),所以直接枚举最长匹配可能的取值再用哈希判断复杂度就对了,\(O(n^2 + n \sum l_i)\)

现在问题就被转换成了如果 \(S_i\) 成为母串开头的字符串,那么母串会增加 \(l_i\) 长度。如果 \(S_j\) 要接在 \(S_i\) 后面,母串最少会增加 \(d(i, j)\) 长度。还是问你母串最短能是多少。

那直接把每个字符串看成一个点,\(d(i, j)\) 看成 \(i\) --> \(j\) 的一条边的边权,从第 \(i\) 个点出发初始有 \(l_i\) 的代价,那么经过所有点恰好一次的通路的最小代价就是母串的最短长度。不难发现实际上就是经典的 TSP 问题,状压 dp 解决之。这部分时间复杂度为 \(O(n^2 \times 2^n)\),所以总复杂度就是 \(O(n^2 + n \sum l_i + n^2 \times 2^n)\),5 秒非常宽松,🐣🐣!

实现

这份代码在作者写这篇题解的时候还是 atc 全站最优解。文章来源地址https://www.toymoban.com/news/detail-837707.html

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

template <class T>
inline void cmin(T &a, const T &b) {
    if (a > b) {
        a = b;
    }
}

const int N = 20, M = 2e5 + 1, Base = 29;

char str[M + 1]; int len[N];
unsigned powBase[M], hashy[N][M];

int dis[N][N], dp[1 << N][N];

int main(int argc, const char * argv[]) {
    powBase[0] = 1;
    for (int i = 1; i < M; ++i) {
        powBase[i] = powBase[i - 1] * Base;
    }
    
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%s", str + 1);
        len[i] = (int)strlen(str + 1);
        for (int j = 1; j <= len[i]; ++j) {
            hashy[i][j] = hashy[i][j - 1] * Base + str[j] - 'a' + 1;
        }
    }
    int del = 0;
    for (int i = 0; i < n; ++i) if (!(del & 1 << i)) {
        for (int j = 0; j < n; ++j) if (i != j && !(del & 1 << j)) {
            int leni = len[i], lenj = len[j];
            for (int k = 1; k <= leni - lenj + 1; ++k) {
                if (hashy[i][k + lenj - 1] - hashy[i][k - 1] * powBase[lenj] == hashy[j][lenj]) {
                    del |= 1 << j;
                    break;
                }
            }
            if (del & 1 << j) {
                continue;
            }
            for (int k = lenj - 1; k >= 0; --k) {
                if (hashy[j][k] == hashy[i][leni] - hashy[i][leni - k] * powBase[k]) {
                    dis[i][j] = lenj - k;
                    break;
                }
            }
        }
    }
    int undel[N], r = 0;
    for (int i = 0; i < n; ++i) if (!(del & 1 << i)) {
        undel[r++] = i;
    }
    n = r;
    int all = (1 << n) - 1;
    for (int i = 1; i <= all; ++i) {
        memset(dp[i], 127, sizeof(int) * (n + 1));
    }
    for (int i = 0; i < n; ++i) {
        dp[1 << i][i] = len[undel[i]];
    }
    for (int state = 1; state < all; ++state) {
        for (int i = state; i; i &= i - 1) {
            int pi = __builtin_ctz(i);
            for (int j = all ^ state; j; j &= j - 1) {
                int pj = __builtin_ctz(j);
                cmin(dp[state | 1 << pj][pj], dp[state][pi] + dis[undel[pi]][undel[pj]]);
            }
        }
    }
    printf("%d\n", *min_element(dp[all], dp[all] + n));
    return 0;
}

到了这里,关于abc343G 题解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [ABC319E] Bus Stops 题解

      给定 (n) 个公交站。对于第 (i) 个公交站,在时刻 (p_i times k,k in mathbb{N}) 有一辆公交车出发,在经过 (t_i) 的时间后,到达第 (i+1) 个公交站。   在走到第一个公交车之前需要走 (X) 时刻,做到最后一个公交站之后下车以后还需要走 (Y) 时刻。   约束: (1

    2024年02月09日
    浏览(28)
  • 题解:ABC320B - Longest Palindrome

    链接:Atcoder。 链接:洛谷。 算法难度:C。 思维难度:C。 调码难度:C。 综合评价:入门。 字符串处理。 通过双层循环分别枚举第一个字符和最后一个字符遍历每个子串,在分别判断是否为回文串,在所有是回文串的里面取长度最大值。 O(|s|2)。 字符串截取用substr函数。

    2024年02月07日
    浏览(33)
  • [ABC318C] Blue Spring 题解

      主人公出去旅游要买票,共有若干天,每天要花不同钱。现在有“通行证”出售,通过购买通行证,可以在某一天直接用通行证,以此来省去当天原本需要花费的票价。通行证只能一套一套买,每套中有 (D) 个,买一套要花费 (P) 元。可以购买任意套数的通行证,求怎

    2024年02月10日
    浏览(28)
  • [AGC055A] ABC Identity 题解

    给定长度为 (3n (1 le n le 2e5)) 的序列,其中字母 A,B,C 各有 (n) 个。 一个合法序列 (T) 满足以下条件: 其长度为 (3k (1 le k le n)) 。 (T_1 = T_2 = ... = T_k) (T_{k + 1} = T_{k + 2} = ... = T_{2k}) (T_{2k + 1} = T_{2k + 2} = ... = T_{3k}) (T_1, T_{k + 1}, T_{2k + 1}) 互不相同。 求一个把这个序列分

    2024年02月08日
    浏览(37)
  • [AGC055B] ABC Supremacy 题解

    给定两个长度为  (n)  的字符串  (a) , (b) 。 你可以进行若干次以下操作: 若  (a)  中的一个 子串 为  ABC , BCA  或  CAB ,那么可以将这个子串替换为  ABC , BCA  或  CAB 。 求能否将  (a)  变成  (b) ,输出  YES  或  NO 。 不难发现,我们可以根据一些变换将 A

    2024年02月08日
    浏览(29)
  • AtCoder abc336 A~D题解

    题目翻译: 对于正整数 X X X 级别的龙串, X X X 是长度为 ( X + 3 ) (X+3) ( X + 3 ) 的字符串,由按此顺序排列的 o 、 n 和 g 的一次 L 、 X X X 次出现形成。 你得到一个正整数 N N N 。打印 N N N 级的龙串。 分析 按题目要求做即可……,输出一个 L ,循环 X X X 次输出 o ,再输出 ng 。

    2024年01月15日
    浏览(30)
  • LeetCode算法题解(动态规划)|LeetCode343. 整数拆分、LeetCode96. 不同的二叉搜索树

    题目链接:343. 整数拆分 题目描述: 给定一个正整数  n  ,将其拆分为  k  个  正整数  的和(  k = 2  ),并使这些整数的乘积最大化。 返回  你可以获得的最大乘积  。 示例 1: 示例 2: 提示: 2 = n = 58 算法分析: 定义dp数组及下标含义: dp[i]表述正整数i拆分成k个正整数

    2024年02月04日
    浏览(30)
  • AT_abc344_e 题解

    本文同步发表于洛谷。 赌狗天天输的一集。 赛时各种【数据删除】原因导致没做出来。 给你一个长度为 (N) 的序列 (A=(A_1,ldots,A_N)) 。保证 (A) 中的元素是不同的。 你要处理 (Q) 个操作。每个操作是以下两种类型之一: 1 x y :在 (A) 中元素 (x) 后面紧接着插入 (y) 。

    2024年03月13日
    浏览(33)
  • AT_abc345_d 题解

    本文同步发表于洛谷。 是个逆天搜索。 最开始:爆搜,启动! 然后 TLE 到飞起。 赛后:我【数据删除】这么简单的吗?! dfs 每个位置,试着把没放过的块放到以这个位置为左上角的区域里面。 好了没了,就是这么简单! 对了记得这个块可以旋转!

    2024年03月21日
    浏览(33)
  • AT_abc344_d 题解

    本文同步发表于洛谷。 赌狗天天输的一集。 你最开始有一个空字符串 (S) 。 你还有编号为 (1, 2, dots, N) 的袋子,每个袋子都包含一些字符串。 袋子 (i) 包含 (A_i) 个字符串 (S_{i,1}, S_{i,2}, dots, S_{i,A_i}) 。 对 (i = 1, 2, dots, N) 重复以下步骤 仅一次 (这里原题没有讲清楚

    2024年03月10日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包