CF1037H Security 后缀自动机 + right集合线段树合并 + 贪心
题目描述:
给定一个字符串 $S$
给出 $Q$ 个操作,给出 $L,R,T$,求出字典序最小的 $S_{1}$ 为 $S[L...R]$的子串,且 $S_{1}$ 的字典序严格大于 $T$. 输出这个 $S_{1}$,如果无解输出 $-1$
不难想到 $S_{1}$ 一定是与 $T$ 有一段相等的前缀并在一个位置上不同.
也就是说,$S_{1}=Prefix(T)+c$ ,$('a'\leqslant c\leqslant'z')$
先考虑没有 $L,R$ 的限制.
先将 $T$ 在后缀自动机上进行匹配,直到匹配不了.
贪心地从后向前枚举不同的那个字符,如果当前找到了不同字符,则直接输出即可. (这一定是最优的,因为前缀更长)
现在有了 $L,R$ 的限制,直接来一遍线段树合并维护 $right$ 集合即可.
在后缀自动机上匹配的时候到线段树中判断一下在不在 $[L,R]$ 中即可.
#include<bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 220000
using namespace std;
int n;
int rt[maxn];
namespace tr
{
#define mid ((l+r)>>1)
#define lson t[x].l
#define rson t[x].r
int cnt;
int newnode() { return ++cnt; }
struct Node{ int l,r,sumv; }t[maxn * 50];
int merge(int u,int v)
{
if(!u||!v) return u+v;
int x=newnode();
t[x].sumv=t[u].sumv+t[v].sumv;
lson=merge(t[u].l,t[v].l);
rson=merge(t[u].r,t[v].r);
return x;
}
void update(int &x,int l,int r,int k,int delta)
{
if(!x)x=newnode();
t[x].sumv+=delta;
if(l==r) return;
if(k<=mid) update(lson, l, mid, k, delta);
else update(rson, mid + 1, r, k, delta);
}
int query(int x,int l,int r,int L,int R)
{
if(!x || L>R)return 0;
if(l>=L&&r<=R) return t[x].sumv;
int tmp=0;
if(L<=mid) tmp+=query(lson,l,mid,L,R);
if(R>mid) tmp+=query(rson, mid+1,r,L,R);
return tmp;
}
#undef lson
#undef rson
};
namespace SAM
{
int tot,last;
int len[maxn], ch[maxn][30], f[maxn], rk[maxn], C[maxn];
void init() { tot = last = 1; }
void extend(int c)
{
int np=++tot,p=last;
last=np, len[np]=len[p]+1;
while(p&&!ch[p][c]) ch[p][c]=np,p=f[p];
if(!p) f[np]=1;
else
{
int q=ch[p][c];
if(len[q]==len[p]+1) f[np]=q;
else
{
int nq=++tot;
len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
f[nq]=f[q],f[np]=f[q]=nq;
while(p&&ch[p][c]==q) ch[p][c]=nq, p=f[p];
}
}
tr::update(rt[np], 1, n, len[np], 1);
}
void prepare()
{
int i,j;
for(i=1;i<=tot;++i) ++C[len[i]];
for(i=1;i<=tot;++i) C[i]+=C[i-1];
for(i=1;i<=tot;++i) rk[C[len[i]]--]=i;
for(i=tot;i>=1;--i)
{
j=rk[i];
rt[f[j]]=tr::merge(rt[f[j]], rt[j]);
}
}
};
char str[maxn], T[maxn], stac[maxn];
int cur[maxn];
int main()
{
int i,j,Q;
// setIO("input");
scanf("%s",str+1);
n=strlen(str+1);
SAM::init();
for(i=1;i<=n;++i)
{
SAM::extend(str[i]-'a');
}
SAM::prepare();
scanf("%d",&Q);
while(Q--)
{
int l,r,_len,trace=0,top=0,L;
scanf("%d%d%s",&l,&r,T+1);
L=l;
_len=strlen(T+1);
cur[0]=1;
for(i=1;i<=min(_len,n-1);++i)
{
int c=T[i]-'a';
if(SAM::ch[cur[i-1]][c] && tr::query(rt[SAM::ch[cur[i-1]][c]], 1, n, l, r))
{
cur[i]=SAM::ch[cur[i-1]][c];
++l, trace=i, stac[++top]=T[i];
}
else break;
}
T[0]='a';
int flag=0;
for(i=min(n-1,trace);i>=0;--i)
{
int c= i + 1 > _len ? -1 : T[i+1] - 'a';
// if(flag) break;
for(j=c+1;j<27;++j)
{
if(SAM::ch[cur[i]][j] && tr::query(rt[SAM::ch[cur[i]][j]] , 1, n, l, r))
{
flag=1;
stac[++top]='a'+j;
break;
}
}
if(flag) break;
--l;
--top;
}
if(!flag) printf("-1\n");
else
{
for(i=1;i<=top;++i) printf("%c",stac[i]);
printf("\n");
}
}
return 0;
}
CF1037H Security 后缀自动机 + right集合线段树合并 + 贪心的更多相关文章
- CF700E Cool Slogans 后缀自动机 + right集合线段树合并 + 树形DP
题目描述 给出一个长度为n的字符串s[1],由小写字母组成.定义一个字符串序列s[1....k],满足性质:s[i]在s[i-1] (i>=2)中出现至少两次(位置可重叠),问最大的k是多少,使 ...
- [BJWC2018]Border 的四种求法(后缀自动机+链分治+线段树合并)
题目描述 给一个小写字母字符串 S ,q 次询问每次给出 l,r ,求 s[l..r] 的 Border . Border: 对于给定的串 s ,最大的 i 使得 s[1..i] = s[|s|-i+ ...
- 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并
题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...
- CF547E Milk and Friends(AC自动机的fail指针上建主席树 或 广义后缀自动机的parent线段树合并)
What-The-Fatherland is a strange country! All phone numbers there are strings consisting of lowercas ...
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...
- 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]
传送门 思路 按照套路,直接上后缀自动机. 部分分:\(l=1,r=|S|\) 首先把\(S\)和\(T\)的后缀自动机都建出来. 考虑枚举\(T\)中的右端点\(r\),查询以\(r\)结尾的串最长 ...
- BZOJ5417[Noi2018]你的名字——后缀自动机+线段树合并
题目链接: [Noi2018]你的名字 题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字 ...
- 【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并
[CF666E]Forensic Examination 题意:给你一个字符串s和一个字符串集合$\{t_i\}$.有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l ...
- [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)
后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...
随机推荐
- Eclipse java web项目 ,导入IntelliJ IDEA 完整操作!
1.如图,这是一个ec项目,是一个ssh框架,搭建一个后台, 我们在idea 新建一个项目:new - project from Existing sources... 要是不放心,你可以做一个文件备 ...
- 前端003/【React + Mobx + NornJ】开发模式
1.React + Mobx + NornJ 开发模式快速上手教程 github网址:https://github.com/joe-sky/nornj-cli/blob/master/docs/gui ...
- JS使用 popstate 事件监听物理返回键
pushHistory(); window.addEventListener("popstate", function (e) { if (or ...
- airtestUI简单操作
touch 判断坐标位置 如touch((500, 600), duration=1) swipe 滑动位置 wait 等待画面出现 exists 判断画面中是否存在某个图片 test 调用输入法,输 ...
- 项目被os x占用
xattr -d com.apple.FinderInfo 空格后拖入项目回车就行了
- Java8 HashMap详解(转)
Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成. 根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们 ...
- P2835 刻录光盘 (tarjan缩点)
[题目描述] 现在假设总共有N个营员(2<=N<=200),每个营员的编号为1~N.LHC给每个人发了一张调查表,让每个营员填上自己愿意让哪些人到他那儿拷贝资料.当然,如果A愿意把资料拷贝 ...
- 两种方法删除ArrayList里反复元素
方法一: /** List order not maintained **/ public static void removeDuplicate(ArrayList arlList) { HashS ...
- spark浅谈(2):SPARK核心编程
一.SPARK-CORE 1.spark核心模块是整个项目的基础.提供了分布式的任务分发,调度以及基本的IO功能,Spark使用基础的数据结构,叫做RDD(弹性分布式数据集),是一个逻辑的数据分区的集 ...
- Sublime-emmet插件的使用
emmet是使用Sublime编写html代码时最好用的一个插件,下面简单介绍一下emmet插件的安装和使用 安装 第一步:打开sublime,首先输入command + shift + p,然后输入 ...