重叠的子串

给定一个长度为n(1≤∣s∣≤105)n(1 \le \mid s \mid \le 10 ^ 5)n(1≤∣s∣≤105)的只由小写字母构成的字符串sss,有m,(1≤m≤106)m, (1 \le m \le 10 ^ 6)m,(1≤m≤106)个询问:

每次询问给定l,rl, rl,r,问sss是否存在一个字串ttt,满足∣t∣<2(r−l+1)\mid t \mid < 2(r - l + 1)∣t∣<2(r−l+1),且s[l,r]s[l, r]s[l,r]在ttt中出现最少两次。

让s[l,r]s[l, r]s[l,r]在ttt上匹配,假设匹配的结束位置为p1,p2,…p_1, p_2, \dotsp1​,p2​,…,如果ttt是一个合法串,则一定存在两个不同的数i,ji, ji,j,

且满足pi≠pjANDabs(pi−pj)<r−l+1p_i \ne p_j \ AND\ abs(p_i - p_j) < r - l + 1pi​​=pj​ AND abs(pi​−pj​)<r−l+1,从这一点出发,我们考虑后缀自动机。

首先我们构建SAMSAMSAM,建出parentparentparent树,我们不难找到endpos=rendpos = rendpos=r的节点,考虑从这个点向上跳,

当我们跳到最上面的满足len≥r−l+1len \ge r - l + 1len≥r−l+1的节点时,显然这个点所代表的endposendposendpos集合中的字串,一定都含有s[l,r]s[l, r]s[l,r]这个后缀。

简单的理解一下也就是s[l,r]s[l, r]s[l,r]可以在这些节点匹配上,所以我们只要判断这些节点是否存在两个endposendposendpos满足其差值≤r−l+1\le r - l + 1≤r−l+1即可。

这里我用的是线段树合并,对线段树上的节点记录其区间最左边有值的树,区间最有边有值的树,

以及当前区间的答案,然后合并上去即可,我们只要记录下每个endposendposendpos集合里面的最小的答案即可,整体复杂度T×O(nlog⁡n+m)T \times O(n \log n + m)T×O(nlogn+m)。

#include <bits/stdc++.h>using namespace std;const int N = 1e5 + 10;int num, last, endpos[N << 1];int head[N << 1], nex[N << 1], to[N << 1], cnt;int fa[N << 1][21], ans[N << 1], idx[N], n, m;int root[N << 1], ls[N << 6], rs[N << 6], tot;char str[N];struct Res {int l, r, minn;
}a[N << 6];struct statu {int len, link, next[26];
}s[N << 1];void push_up(int rt) {if (ls[rt] && rs[rt]) {a[rt] = {a[ls[rt]].l, a[rs[rt]].r, min({a[ls[rt]].minn, a[rs[rt]].minn, a[rs[rt]].l - a[ls[rt]].r})};}else if (ls[rt]) {a[rt] = a[ls[rt]];}else {a[rt] = a[rs[rt]];}
}void update(int &rt, int l, int r, int x) {if (!rt) {rt = ++tot;}if (l == r) {a[rt] = {x, x, 0x3f3f3f3f};return ;}int mid = l + r >> 1;if (x <= mid) {update(ls[rt], l, mid, x);}else {update(rs[rt], mid + 1, r, x);}push_up(rt);
}int merge(int x, int y, int l, int r) {if (!x || !y) {return x | y;}if (l == r) {return x;}int mid = l + r >> 1;ls[x] = merge(ls[x], ls[y], l, mid);rs[x] = merge(rs[x], rs[y], mid + 1, r);push_up(x);return x;
}void add(int x, int y) {to[cnt] = y;nex[cnt] = head[x];head[x] = cnt++;
}void Init() {for (int i = 0; i < num; i++) {s[i].len = s[i].link = endpos[i] = head[i] = root[i] = 0;for (int j = 0; j < 26; j++) {s[i].next[j] = 0;}}num = last = 0;for (int i = 1; i <= tot; i++) {ls[i] = rs[i] = 0;}tot = 0;
}void init() {s[0].len = 0, s[0].link = -1;num++, last = 0, cnt = 1;
}void extend(int c, int id) {int cur = num++;s[cur].len = s[last].len + 1, endpos[cur] = id, idx[id] = cur;int p = last;while (p != -1 && !s[p].next[c]) {s[p].next[c] = cur;p = s[p].link;}if (p == -1) {s[cur].link = 0;}else {int q = s[p].next[c];if (s[p].len + 1 == s[q].len) {s[cur].link = q;}else {int clone = num++;s[clone] = s[q];s[clone].len = s[p].len + 1;while (p != -1 && s[p].next[c] == q) {s[p].next[c] = clone;p = s[p].link;}s[q].link = s[cur].link = clone;}}last = cur;
}void dfs(int rt, int f) {fa[rt][0] = f;for (int i = 1; i <= 20; i++) {fa[rt][i] = fa[fa[rt][i - 1]][i - 1];}for (int i = head[rt]; i; i = nex[i]) {if (to[i] == f) {continue;}dfs(to[i], rt);root[rt] = merge(root[rt], root[to[i]], 1, n);}if (endpos[rt]) {update(root[rt], 1, n, endpos[rt]);}ans[rt] = a[root[rt]].minn;
}void build() {for (int i = 1; i < num; i++) {add(s[i].link, i);}dfs(0, -1);
}int get(int rt, int len) {for (int i = 20; i >= 0; i--) {if (fa[rt][i] != 0 && fa[rt][i] != -1 && s[fa[rt][i]].len >= len) {rt = fa[rt][i];}}return rt;
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);int T;scanf("%d", &T);while (T--) {scanf("%d %d %s", &n, &m, str + 1);init();for (int i = 1; i <= n; i++) {extend(str[i] - 'a', i);}build();for (int i = 1, l, r; i <= m; i++) {scanf("%d %d", &l, &r);int rt = get(idx[r], r - l + 1);if (ans[rt] < r - l + 1) {puts("Yes");}else {puts("No");}}Init();}return 0;
}

2021CCPC华为云挑战赛:HDU 7091 重叠的子串(SAM + 线段树合并)相关推荐

  1. 2021CCPC华为云挑战赛

    2021CCPC华为云挑战赛 A.对象存储调度问题 贪心问题,使用优先队列(大顶堆)存一下分条,然后从小的对象开始填充.如果碰到无法填充的情况直接输出"No",因为堆顶元素是堆里最 ...

  2. 【2021CCPC华为云挑战赛-1006】【HDU-7092】仓颉造数 数学

    2021CCPC华为云挑战赛-1006 仓颉造数 赛时没做出来,当我推出大概结论的时候已经16:02了,这里还是打算把题解写一下,因为感觉官方题解概括性很强所以部分同学 (指我这个菜鸡) 可能看不懂, ...

  3. HDU - 7091 重叠的子串(后缀自动机+set启发式合并+树上倍增)

    题目链接:点击查看 题目大意:给定一个只含小写字母的字符串 sss 和 qqq 次询问,每次询问给定一个字符串,以 s[l..r]s[l..r]s[l..r] 的形式给出,判断 sss 中是否存在两个 ...

  4. 2021CCPC华为云挑战赛热身赛

    A. 题目链接 https://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1001&cid=1030 题意:简单来说必须立足于当前 ...

  5. 2021CCPC华为云挑战赛部分题解

    1001.对象存储调度问题 1001 将 n 个大小为2的整数次幂的数放到 m 个一定空间的分条内,问能否放入. 思路: 首先可以用贪心的思想,把最大的数据往剩余空间最大的分条内放,将数据从大到小排序 ...

  6. 2021CCPC华为云挑战赛1006

    主要是记一点自己不会的东西 这道题化简后就是判断分数a/b化为最简分数后的分数c/d,c+d是否为2的n次方 是就yes,否则no 首先a,b最大公约数,函数gcd 其次判断a是否为2的n次方:a&a ...

  7. 2021CCPC华为云挑战赛 卷业务模型分析

    卷业务模型分析 题目链接 写的时候根本没有想到是最小二乘法,把高中知识全忘了,害 知道是最小二乘法后,那么|B[i]-(k*A[i]+b)|<=10,就可以等价于找到由A1与B ,A2与B所得到 ...

  8. hdu 5511 Minimum Cut-Cut——分类讨论思想+线段树合并

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5511 题意:割一些边使得无向图变成不连通的,并且恰好割了两条给定生成树上的边.满足非树边两段一定在给定生成 ...

  9. HDU - 1255 覆盖的面积(线段树求矩形面积交 扫描线+离散化)

    链接:线段树求矩形面积并 扫描线+离散化 1.给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. 2.看完线段树求矩形面积并 的方法后,再看这题,求的是矩形面积交,类同. 求面积时,用被覆 ...

最新文章

  1. 算法:ACM二分图匹配 HDU2063
  2. 【计算机网络】HTTP 与 HTTPS ( HTTP 在网络各个层级的传输过程 | HTTPS 工作流程 | HTTPS 弊端 )
  3. 数组中其余的排除_胆码中26,或许这样选号更容易中奖!双色球055期红球胆码+大底!(附实战精选)...
  4. 网易云以场景化云服务力拓教育行业
  5. curl 常用的命令
  6. 计算机录音机应用程序在哪,windows7如何给电脑录音 windows7录音机在哪
  7. Eclipse用法和技巧十七:覆盖父类方法
  8. pg数据库表接口和数据导出
  9. Java - HashMap源码解析
  10. 感谢微软BPOS4China技术支持组
  11. win10配置python3虚拟环境_win10下搭建python3+scarpy虚拟环境
  12. 接unityads_[蛮牛教程]unity接入unity Ads详细流程
  13. VC定时器SetTimer函数
  14. linux asio 读取串口,ASIO 串口编程
  15. 推免生是否抢了考研生的“奶酪”
  16. 小米路由固件中lua文件反编译
  17. dependencyManagement和dependencies区别
  18. 爬虫chromedriver被识别怎么办?
  19. shell脚本——sed详细介绍(包含应用案例)
  20. 弘辽科技:不拼低价,95后小伙3个月从0冲上150万

热门文章

  1. 服务器安全维护包含,服务器安全维护包含
  2. mysql查询今天_昨天_7天_近30天_本月_上一月 数据_mysql查询今天、昨天、7天、近30天、本月、上一月 数据...
  3. pandas添加一行数据_恨晚,Python探索性数据分析神器pandas-profiling,一行代码搞定...
  4. 如何巧妙拒绝老同学借钱?哈哈哈哈哈......
  5. 天冷打字全靠抖?!桌面暖手宝,体验10s速热,温暖升级,冬天有TA就够了
  6. 一张图看懂华为计算全联接2020
  7. 看了《隐秘的角落》才知道,掉头发有多可怕!10个掉头发最快的专业!快看看你中枪了没有!...
  8. 中求和符号上下标_涨电脑知识:如何在word中编写复杂的公式,写论文必备技能...
  9. jenkins java反序列化_Jenkins “Java 反序列化”过程远程命令执行漏洞
  10. linux内核线程socket,从Linux源码看Socket(TCP)的accept