「TJOI / HEOI2016」字符串
「TJOI / HEOI2016」字符串
题目描述
佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为 \(n\) 的字符串 \(s\),和 \(m\) 个问题。佳媛姐姐必须正确回答这 \(m\)个问题,才能打开箱子拿到礼物,升职加薪,出任 \(CEO\),嫁给高富帅,走上人生巅峰。每个问题均有 \(a,b,c,d\) 四个参数,问你子串 \(s[a…b]\) 的所有子串和 \(s[c…d]\) 的最长公共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?
\(1 \leq n, m \leq 100000, \ a \leq b, \ c \leq d, \ 1 \leq a, b, c, d \leq n\)
### 解题思路 :
写\(sam\)是肯定会去写的,这样才学的了字符串,后缀数组又不会用,\(sam\)套上数据结构的感觉就像回家一样
里面又能剖分又能线段树合并,调试又好调,我爱死这种写法了 \(qwq\)
问题求一个字符串的前缀最多能和另一个字符串的所有子串匹配多少, 不妨二分答案判断这个前缀是否在这些子串里出现过
考虑对母串建 \(sam\) ,求出原串中每一个后缀在 \(sam\) 上的对应节点,那么对于需要\(check\) 的前缀 \([c, c + len -1]\) ,可以快速倍增找到其在前缀树上对应的节点
设找到的节点为 \(u\) ,问题就转化为 \(u\) 的 \(right\) 集合中,是否存在一个来自于 \([a+len-1, b]\) 的后缀
所以,直接大力线段树合并维护 \(parent\) 树上每个节点的 \(right\) 集合即可,查询只需要判断对应线段树的 \([a+len-1, b]\) 的和是否 \(>=1\),复杂度是 \(O(mlog^2n)\)
/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define N (200005)
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
char s[N]; int n, m;
struct Segment_Tree{
int sum[N*25], lc[N*25], rc[N*25], cnt;
inline void modify(int &u, int l, int r, int pos){
u = ++cnt;
if(l == r) return (void) (sum[u]++);
int mid = l + r >> 1;
if(pos <= mid) modify(lc[u], l, mid, pos);
else modify(rc[u], mid + 1, r, pos);
sum[u] = sum[lc[u]] + sum[rc[u]];
}
inline int merge(int x, int y, int l, int r){
if(!x || !y) return x + y; int o = ++cnt;
if(l == r) sum[o] = sum[x] + sum[y];
else{
int mid = l + r >> 1;
lc[o] = merge(lc[x], lc[y], l, mid);
rc[o] = merge(rc[x], rc[y], mid + 1, r);
sum[o] = sum[lc[o]] + sum[rc[o]];
}
return o;
}
inline int query(int u, int l, int r, int L, int R){
if(!u) return 0;
if(l >= L && r <= R) return sum[u];
int mid = l + r >> 1, res = 0;
if(L <= mid) res += query(lc[u], l, mid, L, R);
if(mid < R) res += query(rc[u], mid + 1, r, L, R);
return res;
}
}Seg;
struct Suffix_Automaton{
int f[N][23], rt[N<<1], buf[N], a[N];
int ch[N][26], fa[N], dep[N], pos[N], tail, size;
inline Suffix_Automaton(){ tail = size = 1; }
inline int newnode(int x){ dep[++size] = x; return size; }
inline void ins(int c, int id){
int p = tail, np = newnode(dep[p] + 1);
Seg.modify(rt[np], 1, n, id), pos[id] = np;
for(; p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
if(!p) return (void) (fa[np] = 1, tail = np);
int q = ch[p][c];
if(dep[q] == dep[p] + 1) fa[np] = q;
else{
int nq = newnode(dep[p] + 1);
fa[nq] = fa[q], fa[q] = fa[np] = nq;
for(int i = 0; i < 26; i++) ch[nq][i] = ch[q][i];
for(; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
}tail = np;
}
inline void prepare(){
for(int i = 1; i <= size; i++) f[i][0] = fa[i];
for(int j = 1; j <= 22; j++)
for(int i = 1; i <= size; i++) f[i][j] = f[f[i][j-1]][j-1];
for(int i = 1; i <= size; i++) buf[dep[i]]++;
for(int i = 1; i <= size; i++) buf[i] += buf[i-1];
for(int i = 1; i <= size; i++) a[buf[dep[i]]--] = i;
for(int i = size; i >= 2; i--){
int u = a[i];
rt[fa[u]] = Seg.merge(rt[u], rt[fa[u]], 1, n);
}
}
inline bool check(int x, int len, int l, int r){
x = pos[x];
for(int i = 22; i >= 0; i--) if(dep[f[x][i]] >= len) x = f[x][i];
return Seg.query(rt[x], 1, n, l, r) >= 1;
}
}van;
inline int solve(int a, int b, int c, int d){
int l = 1, r = min(b - a + 1, d - c + 1), ans = 0;
while(l <= r){
int mid = l + r >> 1;
if(van.check(c + mid - 1, mid, a + mid - 1, b))
ans = mid, l = mid + 1;
else r = mid - 1;
}
return ans;
}
int main(){
read(n), read(m), scanf("%s", s + 1);
for(int i = 1; i <= n; i++) van.ins(s[i] - 'a', i);
van.prepare();
for(int i = 1; i <= m; i++){
int a, b, c, d;
read(a), read(b), read(c), read(d);
printf("%d\n", solve(a, b, c, d));
}
return 0;
}
「TJOI / HEOI2016」字符串的更多相关文章
- loj#2059. 「TJOI / HEOI2016」字符串 sam+线段树合并+倍增
题意:给你一个子串,m次询问,每次给你abcd,问你子串sa-b的所有子串和子串sc-d的最长公共前缀是多长 题解:首先要求两个子串的最长公共前缀就是把反过来插入变成最长公共后缀,两个节点在paren ...
- 【LOJ】#2059. 「TJOI / HEOI2016」字符串
题解 我们冷静一下,先画一棵后缀树 然后发现我们要给c和d这一段区间在[a,b]这一段开头的串里找lcp 而lcp呢,就是c点的祖先的到根的一段,假如这个祖先的子树里有[a,b - dis[u] + ...
- loj2059 「TJOI / HEOI2016」字符串
字符串好难啊不会啊 #include <iostream> #include <cstdio> using namespace std; int n, m, rnk[10000 ...
- loj#2054. 「TJOI / HEOI2016」树
题目链接 loj#2054. 「TJOI / HEOI2016」树 题解 每次标记覆盖整棵字数,子树维护对于标记深度取max dfs序+线段树维护一下 代码 #include<cstdio> ...
- AC日记——#2054. 「TJOI / HEOI2016」树
#2054. 「TJOI / HEOI2016」树 思路: 线段树: 代码: #include <cstdio> #include <cstring> #include < ...
- AC日记——#2057. 「TJOI / HEOI2016」游戏 LOJ
#2057. 「TJOI / HEOI2016」游戏 思路: 最大流: 代码: #include <cstdio> #include <cstring> #include &l ...
- loj #2055. 「TJOI / HEOI2016」排序
#2055. 「TJOI / HEOI2016」排序 题目描述 在 2016 年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他. 这个 ...
- loj2058 「TJOI / HEOI2016」求和 NTT
loj2058 「TJOI / HEOI2016」求和 NTT 链接 loj 思路 \[S(i,j)=\frac{1}{j!}\sum\limits_{k=0}^{j}(-1)^{k}C_{j}^{k ...
- LOJ #2058「TJOI / HEOI2016」求和
不错的推柿子题 LOJ #2058 题意:求$\sum\limits_{i=0}^n\sum\limits_{j=0}^nS(i,j)·2^j·j!$其中$ S(n,m)$是第二类斯特林数 $ Sol ...
随机推荐
- 编写Linux脚本
下面是重新启动Linux下某进程的shell脚本.以tomcat进程为例: #!/bin/sh pid=`ps -ef|grep tomcat|grep -v grep|awk '{print $2} ...
- redis系列--你真的入门了吗?redis4.0入门~
前言 redis作为nosql家族中非常热门的一员,也是被大型互联网公司所青睐,无论你是开发.测试或者运维,学习掌握它总会为你的职业生涯增色添彩. 当然,你或多或少已经了解redis,但是你是否了解其 ...
- 20155217《网络对抗》Exp05 MSF基础应用
20155217<网络对抗>Exp05 MSF基础应用 实践内容 本实践目标是掌握metasploit的基本应用方式,重点常用的三种攻击方式的思路.具体需要完成: 一个主动攻击实践,如ms ...
- 20155236范晨歌_Web基础
20155236范晨歌_Web基础 目录 实践目标 Apache 前端编程 后端编程 PHP MYSQL & 后端 简单SQL注入与XSS 发帖和会话管理 实践目标 (1)Web前端HTML ...
- 通过定义过滤器filter解决跨域问题
跨域是比较常见问题,比较简单的方式就是直接定义一个过滤器filter,然后在请求头里面加上一些参数.下面来看看具体的写法吧. 一.java代码 package com.hj.usera ...
- 微信小程序之用户信息授权 wx.getUserInfo
用户授权 <button open-type="getUserInfo" bindgetuserinfo='getUser'>授权用户信息</button> ...
- python中eval函数作用
eval函数就是实现list.dict.tuple与str之间的转化str函数把list,dict,tuple转为为字符串 一.字符串转换成列表 a = "[[1,2], [3,4], [5 ...
- 4字节emoji表情对应的Unicode编码获取和编码转换
GitHub Flavored Markdown 今天研究了一天Markdown移动端和pc端统一实现方式,由于以前有搞过移动端富文本编辑器,搞Markdown简单多了: 其中GFM的表情语法不错,比 ...
- 蓝牙学习笔记二(Android连接问题)
可以通过以下两点加速蓝牙连接: 1.更新连接参数 interval:连接间隔(connection intervals ),范围在 7.5 毫秒 到 4 秒. latency:连接延迟 ... 还有一 ...
- 我的SQL SERVER数据库会装满吗?
概述 今天有个客户问我一个蛮有意思的问题.我使用的SQL SERVER 2008数据库,目前数据库130多G,其中某个表的记录条数就有3亿1千多万,占用了50多G.那SQL SERVER 数据库中的表 ...