1 条题解
-
0
自动搬运
来自洛谷,原作者为

wlzhouzhuan
一直在路上。搬运于
2025-08-24 21:49:36,当前版本为作者最后更新于2019-07-13 12:53:54,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
讲一下自己的大致思路。
先说一下暴力分:
这是我看到此题的第一反应:
dp,复杂度为。我们先定义两字符串间的距离值,表示号串拼接在号串之后,需要在号串末尾添加的字符数。
此时我们再用 表示这个串里应经有了个字符串,并且最后出现在该串里的子串是号串的答案,显然可以按下转移:
,表示取1个字符串,并且是第个字符串结尾,那么它的代价显然是
,我们在号字符串 后面拼接上号字符串,添加字符量即为。
注意转移时的顺序,应该先枚举,再枚举和。读者可以想想,为什么?
贴一下
dp的核心部分:void DP() { memset(dp, 0x7f, sizeof(dp)); for (int i = 1; i <= n; i++) dp[1][i] = len[i]; for (int i = 2; i <= m; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= n; k++) dp[i][j] = min(dp[i][j], dp[i - 1][k] + dis[k][j]); }所以,我们分析想到了转移公式,那么答案即为,其中(因为一共有种字符串)。
看到有个字符串,并且是一个个字符串首尾拼接而成的,我们很自然的想到
AC自动机。在本人经过长达1小时的思考后,终于想出了正解。。。
本人比较擅长
KMP算法,看到这题,果断想到是KMP,粗略计算一下,KMP()函数部分的复杂度为$O(\sum^{n}_{i=1}\sum^{n}_{j=1}(len(i)+len(j)))= O(\sum^{n}_{i=1}(n\times len[i]+\sum |S|))≈ O(n|S|)$,稍微带一点小常数。下面是
KMP部分的代码:void KMP() { // kmp1:单个字符串内匹配 for (int p = 1; p <= n; p++) { int j = 0; for (int i = 2; i <= len[p]; i++) { while (j > 0 && str[p][j + 1] != str[p][i]) j = nxt[p][j]; if (str[p][j + 1] == str[p][i]) j++; nxt[p][i] = j; } } // kmp2:两两字符串间匹配 for (int p1 = 1; p1 <= n; p1++) { for (int p2 = 1; p2 <= n; p2++) { int j = 0; for (int i = 2; i <= len[p1]; i++) { while (j > 0 && str[p2][j + 1] != str[p1][i]) j = nxt[p2][j]; if (str[p2][j + 1] == str[p1][i]) j++; if (i == len[p1]) { a.v[p1][p2] = len[p2] - j; } } } } }好,既然我们已经预处理
KMP得到了两两字符串间的关系(末尾添加字符量),那么我们接下来考虑如何优化dp。读者不妨再回头去看看
dp的转移部分,有没有发现,这个转移方式似曾相识?没错,这似乎与
floyd的转移代码有异曲同工之妙。它的转移方式,跟floyd是一模一样的!那么这个转移方式等价于一个的floyd,我们从另一种角度去考虑如何优化这个伪的floyd。在优化复杂度之前,请读者先考虑一些细节问题:
首先,将这个转换成
floyd,是基于点操作的,我们可以将每一个字符串的每一个字符都视作是一个点,这下次就是彻彻底底的floyd了233。其次,我们不知道该从第几号字符串作为整一个串的起点,所以我们考虑加一个
超级源,设它为号串,将号串连接到第号串。从到的权值为,从到显然做不到,所以设置为无穷大,即:。好了,接下来我们考虑如何去优化复杂度。
很显然,数据范围中的范围特别大,已经到了的地步,此时我们就数据范围倒推,显然不可能是线性扫,所以极大可能是或者。
如果是,那么一定有关于开根号的东西,但
floyd显然不可能与根号发生关系,所以暂时排除。那么我们发现只可能是的复杂度内运行
floyd,此时我们果断想到:与有关的,极大可能是快速幂,但这不是的形式,而是一个二维的幂形式。相信读者都已经猜到了,这就是
矩阵快速幂!综上,我们发现,这道题可以在的复杂度内跑出结果,常数较小,值得一试。
同时,作为前人,也提醒一下大家:这道题可能要开才能,
数据#1稍微有点卡常数!献上代码,完美撒花:
#include <bits/stdc++.h> using namespace std; #define int long long typedef long long ll; const int N = 202; const int M = 100002; string str[N]; int len[N], t; int nxt[N][M]; //int dis[201][201]; int n, m; struct MAT { ll v[N][N]; void print() { for (int i = 0; i <= n; i++, puts("")) for (int j = 0; j <= n; j++) printf("%lld ", v[i][j]); putchar('\n'); } } ans, a; MAT operator * (MAT a, MAT b) { MAT c; for (int i = 0; i <= n; i++) for (int j = 0; j <= n; j++) c.v[i][j] = 1e15; for (int i = 0; i <= n; i++) for (int j = 0; j <= n; j++) for (int k = 0; k <= n; k++) c.v[i][k] = min(c.v[i][k], a.v[i][j] + b.v[j][k]); return c; } void KMP() { // init a.v[0][0] = 1e15; for (int p = 1; p <= n; p++) a.v[0][p] = len[p], a.v[p][0] = 1e15; // kmp1 for (int p = 1; p <= n; p++) { int j = 0; for (int i = 2; i <= len[p]; i++) { while (j > 0 && str[p][j + 1] != str[p][i]) j = nxt[p][j]; if (str[p][j + 1] == str[p][i]) j++; nxt[p][i] = j; } } // kmp2 for (int p1 = 1; p1 <= n; p1++) { for (int p2 = 1; p2 <= n; p2++) { int j = 0; for (int i = 2; i <= len[p1]; i++) { while (j > 0 && str[p2][j + 1] != str[p1][i]) j = nxt[p2][j]; if (str[p2][j + 1] == str[p1][i]) j++; if (i == len[p1]) { a.v[p1][p2] = len[p2] - j; } } } } /* for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) printf("dis[%d][%d] = %d\n", i, j, dis[i][j]); */ } void ksm() { //a.print(); for (int i = 0; i <= n; i++) for (int j = 0; j <= n; j++) ans.v[i][j] = a.v[i][j]; // woc, m--! m--; while (m > 0) { //printf("in ksm %d\n", m); //ans.print(); a.print(); //printf("----------\n"); if (m & 1) ans = ans * a; a = a * a; m >>= 1; } } bool check() { for (int i = 1; i <= n; i++) { if (len[i] == 1) return 1; } return 0; } signed main() { scanf("%lld%lld", &n, &m); if (m == 0) { puts("0"); return 0; } for (int i = 1; i <= n; i++) { cin >> str[i]; len[i] = str[i].size(); str[i] = ' ' + str[i]; } if (check()) { printf("%lld\n", m); return 0; } KMP(); ksm(); ll anss = 2e15; for (int i = 1; i <= n; i++) anss = min(anss, ans.v[0][i]); printf("%lld\n", anss); return 0; }附:如果有的网友,我在这里提供几组数据,您可以不妨一测。
5 100 a b c d e1001 0 a04 5 ab bc cd de7
- 1
信息
- ID
- 2578
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者