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\),然后在主席树 ...
随机推荐
- 类InetAddress
如果一个类没有构造方法:A:成员全部是静态的(Math,Arrays,Collections)B:单例设计模式(Runtime)C:类中有静态方法返回该类的对象(InetAddress) public ...
- window10 本地搭建SVN服务器
window10本地搭建SVN服务器教程:https://www.cnblogs.com/warmlight/p/11075644.html 我按照这篇教程成功搭建SVN服务器. 有个比较坑的地方是w ...
- node+express POST请求
// POST 登录 app.post('/login', function (req, res) { // 定义了一个post变量,用于暂存请求体的信息 let [post,addSql,addSq ...
- unity不规则按钮解决方案
一种是alpha检测 一种是设置collider 参考: https://zhuanlan.zhihu.com/p/34204396 下面给出第二种方案代码 ///按钮多边形点击方案,注意Canvas ...
- Ubuntu操作系统的总结操作
一.Ubuntu系统环境变量 Ubuntu Linux系统环境变量配置文件分为两种:系统级文件和用户级文件 1.系统级文件: /etc/profile:在登录时,操作系统定制用户环境时使用的第一个文件 ...
- Parameter setting for Jemeter Post method
1. create CSV file note: the first line is parameter name 2. Add Controller Edit >Add >Logic C ...
- python控制流-提前结束进程
一.sys.exit() 调用 sys.exit()函数,可以让程序终止或退出. 这个函 数在 sys 模块中,必须先导入 sys,才能使用它: #!/usr/bin/env python #codi ...
- C++调用C#类库函数
最近做一个信息化三维仿真项目,基于第三方提供的虚拟引擎通过VC++2008做二次开发,其中涉及到与C#客户端的融合以及数据交互的问题, 主要是VC++需要调用C#客户端提供的类库内的接口获取C#客户端 ...
- C++ 14 新特性总结
转载自: http://www.codeceo.com/article/cpp-14-new-features.html C++14 这一继C++11 之后的新的 C++ 标准已经被正式批准,正在向 ...
- Mock接口数据 = mock服务 + iptable配置
一.mock接口数据应用场景: 1.测试接口A,A接口代码中调用其他服务的B接口,由于开发排期.测试环境不通等原因,依赖接口不可用 2.测试异常情况,依赖接口B返回的数据格式不对.返回None.超时等 ...