当前位置: 首页 > news >正文

P3269 [JLOI2016] 字符串覆盖题解

hhh(鼠鼠第一篇黑题题解)

题目传送门

题目描述

字符串 A 有 N 个子串 B1​,B2​,...,Bn​。如果将这 n 个子串分别放在恰好一个它在 A 中出现的位置上(子串之间可以重叠)这样 A 中的若干字符就被这 N 个子串覆盖了。问 A 中能被覆盖字符个数的最小值和最大值。

输入格式

第一行包含一个正整数 T,表示数据组数。保证 T≤10。

接下来依次描述 T 组数据,每组数据中:

第一行包含一个由小写字母组成的字符串,表示母串 A。

第二行包含一个整数 N,表示子串的个数。

接下来 N 行,每行包含一个由小写字母组成的字符串,描述子串。数据保证所有子串均在母串中出现。

输出格式

输出为 T 行,对应每组数据的答案。每行包含两个整数 Minans 和 Maxans,分别表示对应数据中能被覆盖字符数量的最小值和最大值。

输入输出样例

输入 #1

2
hello
4
he
l
l
o
abacaba
4
ab
ba
a
c

输出 #1

4 5 
4 6

说明/提示

字符串长度 A≤10000,N≤4,子串长度≤10000。

通过上面的阅读可以知道,这道题已经算黑题中还行的了。

但我们如何做这题呢?(明示*1)

为啥是字符串覆盖呢?(明示*2)

最大值最小值咋求呢?(明示*3)

非常显然:最大值最小值需要分别来算(除非是别的方法,如果没想出来这点的话……考虑一下上个班吧……)

首先呢,我们需要设一些辅助数组:n 个子串 si​ 的 next 数组(KMP)以及与母串 T 在每个位置的匹配情况。在优化之后,复杂度来到O(nL),时限1秒,够了。

1.最大值

遇到这种题目我们似乎无从下手,那么尝试把 n=4 作为突破口。考虑 n! 枚举钦定每个字符串出现位置按开头从左到右的顺序,那么一个贪心的想法是把出现顺序在前面的字符串尽量往前放。但这样有个问题,就是在放第 i 个字符串时有两种情况:是否与 si−1​ 重叠,因为两种情况都有可能成为最优解(反例容易举出)。但若确定了是哪种情况,贪心策略就保证了方案唯一:若不重叠,则越往前放越好(给剩下来的字符串留足空间);若重叠则越往后放越好(因为不劣)。因此再 2n−1 枚举相邻的两个字符串是否重叠即可。注意统计答案是不应只关注前一个字符串,因为可能出现 l1​<l2​<r2​<l3​<r3​<r1​ 的情况,其中 li​,ri​ 是 si​ 在 T 中的出现位置,因此需记录的是当前所有字符串的右端点最大值即 maxri​。时间复杂度 O(n!2nnL)。

当然可以更优:用 log 级别的查找即 lower_bound 代替线性查找即可做到 O(nL+n!2nnlogL)。

2.最小值

一个显然的想法是舍弃所有被其它字串覆盖的子串,若相同则仅保留一个,因为要使答案最小让其被完全覆盖一定最优。那么剩下来的子串就一定满足若 li​<lj​ 则一定有 ri​<rj​,这是很强的一个性质,并且结合最优化的限制,给予我们动态规划的思想:设 fi,S​ 表示前 i 位放置了集合 S 内的子串的最小值且第 i 位被覆盖,转移时枚举 p∈S 且 sp​ 与 T 在 i 处匹配。分两种情况讨论,一种是与已放置字符串有交集,另一种是不交,综合一下转移方程如下:

checkmin(fi,S​,p∈Smin​0≤j<imin​fj,S\p​+min(lenp​,i−j))

当 j>i−lenp​ 时 i−j<lenp​ 故进行贡献为 lenp​ 的转移不会使答案变得更小(即更优),而当 j≤i−lenp​ 时 i−j>lenp​ 所以进行贡献为 i−j 的转移也不会影响答案,因此可以看做对于每个 j∈[0,i) 都进行 lenp​ 和 i−j 的转移。lenp​ 可以通过直接记录 fi,S​ 前缀最小值优化,而 i−j 的转移可以设 gi,S​ 表示 minj=0i​fj,S​−j 进行优化。再加上滚动数组,本部分时间复杂度 O(n2nL),空间复杂度更是仅有惊人的 O(2n)!

也许你会问:直接用求最小值的 DP 求最大值不就行了吗?非也,因为转移方程中 min(lenp​,i−j) 的部分并没有变成 max,故此时 lenp​ 只能从 j≤i−lenp​ 转移,而 i−j 只能从 j>i−lenp​ 转移,所以需要加一个线段树维护区间修改与区间最值,很麻烦,不如直接贪心更方便。而且一道题目锻炼两种思维,岂不妙哉?

复杂度分析:本题的时间复杂度为 O(n!n2nlogL+n2nL),空间复杂度为 O(nL+2n)。很显然后者已经达到了理论下界。实现起来不算麻烦,而且效率非常优秀。

得吃代码:

#include <bits/stdc++.h>
using namespace std;
#define mem(x, v, s) memset(x, v, sizeof(x[0]) * (s))
template <class T1, class T2> void cmin(T1 &a, T2 b){a = a < b ? a : b;}
template <class T1, class T2> void cmax(T1 &a, T2 b){a = a > b ? a : b;}
const int N = 1e4 + 5;
char t[N], s[4][N];
int n, tL, len[4], nxt[4][N];
bool mat[4][N];
void KMP(char *s, int sL, int *nxt, bool *mat) {for(int i = 2; i <= sL; i++) {nxt[i] = nxt[i - 1];while(nxt[i] && s[nxt[i] + 1] != s[i]) nxt[i] = nxt[nxt[i]];if(s[nxt[i] + 1] == s[i]) nxt[i]++;}for(int i = 1, p = 0; i <= tL; i++) {while(p && s[p + 1] != t[i]) p = nxt[p];if(s[p + 1] == t[i]) p++;if(p == sL) mat[i] = 1, p = nxt[p];else mat[i] = 0;}
}
bool OverLap(char *t, char *s, int tL, int sL, int *nxt) {for(int i = 1, p = 0; i <= tL; i++) {while(p && s[p + 1] != t[i]) p = nxt[p];if(s[p + 1] == t[i]) p++;if(p == sL) return 1;}return 0;
}
int GetMax() {if(n == 1) return len[0];static int id[4], ans, pos[4][N], cnt[4]; ans = 0, mem(cnt, 0, 4);for(int i = 0; i < n; i++) id[i] = i;for(int i = 0; i < n; i++) for(int j = 1; j <= tL; j++) if(mat[i][j]) pos[i][cnt[i]++] = j - len[i];do {for(int S = 0; S < 1 << n - 1; S++) {int cur = -1, res = 0, rbound = 0;for(int bit = 0; bit < n; bit++) {int i = id[bit];if(!bit) {cur = pos[i][0], rbound = cur + len[i] - 1, res = len[i]; continue;}int p = -1, pr = id[bit - 1];if(S >> bit - 1 & 1) {int rlim = min(tL - len[i] + 1, cur + len[pr] - 1);int it = upper_bound(pos[i], pos[i] + cnt[i], rlim) - pos[i];if(it == 0 || pos[i][it - 1] < cur) break;p = pos[i][it - 1];}else {int it = lower_bound(pos[i], pos[i] + cnt[i], cur + len[pr]) - pos[i];if(it == cnt[i]) break;p = pos[i][it];}res += max(0, p + len[i] - 1 - max(rbound, p - 1));cmax(rbound, p + len[i] - 1), cur = p;}cmax(ans, res);}} while(next_permutation(id, id + n));return ans;
}
int GetMin() {if(n == 1) return len[0];static int ban[4], id[4], m; mem(ban, 0, 4), m = 0;for(int i = 0; i < n; i++) for(int j = 0; j < n; j++)if(strcmp(s[i] + 1, s[j] + 1)) ban[j] |= OverLap(s[i], s[j], len[i], len[j], nxt[j]);for(int i = 0; i < n; i++) for(int j = i + 1; j < n; j++) ban[j] |= !strcmp(s[i] + 1, s[j] + 1);for(int i = 0; i < n; i++) if(!ban[i]) id[m++] = i;static int f[2][16], g[2][16];mem(f, 0x3f, 2), mem(g, 0x3f, 2), f[0][0] = g[0][0] = 0;for(int i = 1, cur = 1, pr = 0; i <= tL; i++, swap(cur, pr)) {for(int j = 0; j < 1 << m; j++) {f[cur][j] = N;for(int k = 0; k < j; k++) {if(!(j >> k & 1)) continue;int S = j - (1 << k), p = id[k];if(mat[p][i]) cmin(f[cur][j], min(f[pr][S] + len[p], g[pr][S] + i));}g[cur][j] = min(g[pr][j], f[cur][j] - i), cmin(f[cur][j], f[pr][j]);}}return f[tL & 1][(1 << m) - 1];
}
void solve() {scanf("%s %d", t + 1, &n), tL = strlen(t + 1);for(int i = 0; i < n; i++) {scanf("%s", s[i] + 1);KMP(s[i], len[i] = strlen(s[i] + 1), nxt[i], mat[i]);}cout << GetMin() << " " << GetMax() << "\n";
}
int main(){int T; cin >> T;while(T--) solve();return 0;
}

感谢观看!!!

http://www.dtcms.com/a/500128.html

相关文章:

  • C++IO库
  • 大型网站开发 框架wordpress俄语版
  • 贵州省交通工程建设质监局网站深圳市网站备案
  • 微信息公众平台微网站建设网站开发与运行环境
  • 智谱大模型实现文生视频案例
  • 有关网站建设的参考书慧谷网站开发文档
  • 中英文网站建设方案广州搜索引擎优化
  • 这么做网站原型图网站建设公司倒闭
  • 网站建设功能怎么写深圳seo培训
  • 算法题(237):滑雪
  • MQTT 协议深度学习笔记(含实战示例・完整版)
  • 工程建设网站导航图珠海建网站价格
  • 做外贸是什么网站网络宣传网站建设制作
  • 网站关键词制作《电子商务网站开发与管理》书籍
  • 成绩查询系统网站开发怎么做网站的图片跳转
  • 基于 GEE MODIS 数据实现 7 大遥感指数计算与可视化
  • 【计算机算法设计与分析】分治算法
  • CSS核心概念全解析:从入门到精通
  • 公司品牌网站建设常州语言网站建设
  • 北京做商铺的网站网站建设及域名申请 厦门
  • 微网站制作软件无版权视频素材网站
  • 深圳外贸建站模版那些市区做网站群
  • 【Linux】路劲解析-简析inode和dentry关系
  • AI Agent概念 原理 实践
  • 微信公众号的网站开发四川建设厅官方网站文件下载
  • 提供五屏网站建设深圳外贸建站网络推广价格
  • 电脑的 wifi 图标不见了该怎么处理
  • 深入浅出:SQL注入中的逗号绕过技巧剖析
  • (Kotlin高级特性四)kotlin属性委托(如 by lazy) 的原理?
  • 网站美术视觉效果布局设计在线服务平台的跨境电商有哪些