CodeForces - 666E: Forensic Examination (广义SAM 线段树合并)
题意:给定字符串S,然后M个字符串T。Q次询问,每次给出(L,R,l,r),问S[l,r]在L到R这些T字符串中,在哪个串出现最多,以及次数。
思路:把所有串建立SAM,然后可以通过倍增走到[l,r]在SAM上的位置p,然后在这个位置p上求,求的过程就是一个线段树求区间最值。 现在的关键是得到线段树,这个线段树记录了endpos,这个可以用启发式合并。
注意现在是广义后缀自动机,不能用拓扑排序合并线段树。 必须用fail树DFS来合并。 不然会出错。 具体我也不知道,不过我估计是因为广义自动机里面有空串导致的。 因为空串导致x和fax的长度相同,这样拓扑就会出错。 而普通的SAM没有这种情况。
#include<bits/stdc++.h>
#define rep2(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=;
char c[maxn],t[maxn];
int rt[maxn],pos[maxn],fcy[maxn],N,M,Q,tot;
int f[maxn][],ans,num;
struct in{
int L,R,Mx,id;
}s[maxn*];
void update(int now)
{
if(s[s[now].L].Mx>=s[s[now].R].Mx) {
s[now].Mx=s[s[now].L].Mx;
s[now].id=s[s[now].L].id;
}
else {
s[now].Mx=s[s[now].R].Mx;
s[now].id=s[s[now].R].id;
}
}
void ins(int &now,int L,int R,int p)
{
if(!now) now=++tot;
if(L==R){ s[now].Mx++; s[now].id=L; return ;}
int Mid=(L+R)>>;
if(p<=Mid) ins(s[now].L,L,Mid,p);
else ins(s[now].R,Mid+,R,p);
update(now);
}
void merge(int &now,int x,int y,int L,int R)
{
if(!x||!y) { now= x|y; return ;}
now=++tot;int Mid=(L+R)>>;
if(L==R) { s[now].Mx=s[x].Mx+s[y].Mx; s[now].id=L; return ;}
merge(s[now].L,s[x].L,s[y].L,L,Mid);
merge(s[now].R,s[x].R,s[y].R,Mid+,R);
update(now);
}
void query(int Now,int L,int R,int l,int r)
{
if(!Now||l>r) return ;
if(s[Now].Mx<num) return ;
if(l<=L&&r>=R) {
if(s[Now].Mx>num) num=s[Now].Mx,ans=s[Now].id;
else if(s[Now].Mx==num&&num!=) ans=min(ans,s[Now].id);
return ;
}
int Mid=(L+R)>>;
if(l<=Mid) query(s[Now].L,L,Mid,l,r);
if(r>Mid) query(s[Now].R,Mid+,R,l,r);
}
struct SAM{
int ch[maxn][],fa[maxn],maxlen[maxn],cnt,last;
int a[maxn],b[maxn];
void init()
{
cnt=last=;
memset(ch[],,sizeof(ch[]));
}
void add(int x,int id)
{
int np=++cnt,p=last; last=np;
if(id>) ins(rt[np],,M,id);
maxlen[np]=maxlen[p]+; memset(ch[np],,sizeof(ch[np]));
while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p];
if(!p) fa[np]=;
else {
int q=ch[p][x];
if(maxlen[q]==maxlen[p]+) fa[np]=q;
else {
int nq=++cnt; maxlen[nq]=maxlen[p]+;
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][x]==q) ch[p][x]=nq,p=fa[p];
}
}
}
vector<int>G[maxn];
void dfs(int u)
{
for(int i=;i<G[u].size();i++){
int v=G[u][i];
f[v][]=u; dfs(v);
merge(rt[u],rt[u],rt[v],,M);
}
}
void DFS()
{
rep(i,,cnt) G[fa[i]].push_back(i);
dfs();
}
}T;
void solve()
{
int L,R,l,r,len;
scanf("%d%d%d%d",&L,&R,&l,&r);
len=r-l+;
int now=pos[r];
if(T.maxlen[now]<len) {
printf("%d %d\n",L,);
return ;
}
for(int i=;i>=;i--)
if(T.maxlen[f[now][i]]>=len) now=f[now][i];
ans=L; num=;
query(rt[now],,M,L,R);
printf("%d %d\n",ans,num);
}
int main()
{
T.init();
scanf("%s",t+);
N=strlen(t+); T.last=;
rep(i,,N) T.add(t[i]-'a',),pos[i]=T.last;
scanf("%d",&M);
rep(i,,M) {
T.last=;
scanf("%s",c+); N=strlen(c+);
rep(j,,N) T.add(c[j]-'a',i);
}
T.DFS();
rep(i,,) rep(j,,T.cnt) f[j][i]=f[f[j][i-]][i-];
scanf("%d",&Q);
while(Q--) solve();
return ;
}
稍微优化一下,为了省空间和时间,也可以手动插入S,即S没有必要插入SAM中。
#include<bits/stdc++.h>
#define rep2(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=;
char c[maxn],t[maxn];
int rt[maxn],pos[maxn],fcy[maxn],N,M,Q,tot;
int f[maxn][],ans,num;
struct in{
int L,R,Mx,id;
}s[maxn*];
void update(int now)
{
if(s[s[now].L].Mx>=s[s[now].R].Mx) {
s[now].Mx=s[s[now].L].Mx;
s[now].id=s[s[now].L].id;
}
else {
s[now].Mx=s[s[now].R].Mx;
s[now].id=s[s[now].R].id;
}
}
void ins(int &now,int L,int R,int p)
{
if(!now) now=++tot;
if(L==R){ s[now].Mx++; s[now].id=L; return ;}
int Mid=(L+R)>>;
if(p<=Mid) ins(s[now].L,L,Mid,p);
else ins(s[now].R,Mid+,R,p);
update(now);
}
void merge(int &now,int x,int y,int L,int R)
{
if(!x||!y) { now= x|y; return ;}
now=++tot;int Mid=(L+R)>>;
if(L==R) { s[now].Mx=s[x].Mx+s[y].Mx; s[now].id=L; return ;}
merge(s[now].L,s[x].L,s[y].L,L,Mid);
merge(s[now].R,s[x].R,s[y].R,Mid+,R);
update(now);
}
void query(int Now,int L,int R,int l,int r)
{
if(!Now||l>r) return ;
if(s[Now].Mx<num) return ;
if(l<=L&&r>=R) {
if(s[Now].Mx>num) num=s[Now].Mx,ans=s[Now].id;
else if(s[Now].Mx==num&&num!=) ans=min(ans,s[Now].id);
return ;
}
int Mid=(L+R)>>;
if(l<=Mid) query(s[Now].L,L,Mid,l,r);
if(r>Mid) query(s[Now].R,Mid+,R,l,r);
}
struct SAM{
int ch[maxn][],fa[maxn],maxlen[maxn],cnt,last;
int a[maxn],b[maxn];
void init()
{
cnt=last=;
memset(ch[],,sizeof(ch[]));
}
void add(int x,int id)
{
int np=++cnt,p=last; last=np;
if(id>) ins(rt[np],,M,id);
maxlen[np]=maxlen[p]+; memset(ch[np],,sizeof(ch[np]));
while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p];
if(!p) fa[np]=;
else {
int q=ch[p][x];
if(maxlen[q]==maxlen[p]+) fa[np]=q;
else {
int nq=++cnt; maxlen[nq]=maxlen[p]+;
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][x]==q) ch[p][x]=nq,p=fa[p];
}
}
}
vector<int>G[maxn];
void dfs(int u)
{
for(int i=;i<G[u].size();i++){
int v=G[u][i];
f[v][]=u; dfs(v);
merge(rt[u],rt[u],rt[v],,M);
}
}
void DFS()
{
rep(i,,cnt) G[fa[i]].push_back(i);
dfs();
}
}T;
void solve()
{
int L,R,l,r,len;
scanf("%d%d%d%d",&L,&R,&l,&r);
len=r-l+;
int now=pos[r];
if(fcy[r]<len) {
printf("%d %d\n",L,);
return ;
}
for(int i=;i>=;i--)
if(T.maxlen[f[now][i]]>=len) now=f[now][i];
ans=L; num=;
query(rt[now],,M,L,R);
printf("%d %d\n",ans,num);
}
int main()
{
T.init();
scanf("%s",t+);
scanf("%d",&M);
rep(i,,M) {
T.last=;
scanf("%s",c+); N=strlen(c+);
rep(j,,N) T.add(c[j]-'a',i);
}
int now=,len=; N=strlen(t+);
rep(i,,N) {
int x=t[i]-'a';
if(T.ch[now][x]) now=T.ch[now][x],len++;
else {
while(now&&!T.ch[now][x]) now=T.fa[now];
if(now==) now=,len=;
else len=T.maxlen[now]+,now=T.ch[now][x];
}
pos[i]=now;
fcy[i]=len;
}
T.DFS();
rep(i,,) rep(j,,T.cnt) f[j][i]=f[f[j][i-]][i-];
scanf("%d",&Q);
while(Q--) solve();
return ;
}
CodeForces - 666E: Forensic Examination (广义SAM 线段树合并)的更多相关文章
- CF666E-Forensic Examination【广义SAM,线段树合并】
正题 题目链接:https://www.luogu.com.cn/problem/CF666E 解题思路 给出一个串\(S\)和\(n\)个串\(T_i\).\(m\)次询问\(S_{a\sim b} ...
- 【Codeforces666E】Forensic Examination 后缀自动机 + 线段树合并
E. Forensic Examination time limit per test:6 seconds memory limit per test:768 megabytes input:stan ...
- 【Codeforces 1037H】Security(SAM & 线段树合并)
Description 给出一个字符串 \(S\). 给出 \(Q\) 个操作,给出 \(L, R, T\),求字典序最小的 \(S_1\),使得 \(S^\prime\) 为\(S[L..R]\) ...
- CF204E-Little Elephant and Strings【广义SAM,线段树合并】
正题 题目链接:https://www.luogu.com.cn/problem/CF204E 题目大意 \(n\)个字符串的一个字符串集合,对于每个字符串求有多少个子串是这个字符串集合中至少\(k\ ...
- YbtOJ#532-往事之树【广义SAM,线段树合并】
正题 题目链接:https://www.ybtoj.com.cn/problem/532 题目大意 给出\(n\)个点的一个\(Trie\)树,定义\(S_x\)表示节点\(x\)代表的字符串 求$$ ...
- Codeforces 1276F - Asterisk Substrings(SAM+线段树合并+虚树)
Codeforces 题面传送门 & 洛谷题面传送门 SAM hot tea %%%%%%% 首先我们显然可以将所有能够得到的字符串分成六类:\(\varnothing,\text{*},s, ...
- Codeforces 700E. Cool Slogans 字符串,SAM,线段树合并,动态规划
原文链接https://www.cnblogs.com/zhouzhendong/p/CF700E.html 题解 首先建个SAM. 一个结论:对于parent树上任意一个点x,以及它所代表的子树内任 ...
- Codeforces.666E.Forensic Examination(广义后缀自动机 线段树合并)
题目链接 \(Description\) 给定串\(S\)和\(m\)个串\(T_i\).\(Q\)次询问,每次询问\(l,r,p_l,p_r\),求\(S[p_l\sim p_r]\)在\(T_l\ ...
- Codeforces 666E Forensic Examination SAM or SA+线段树合并
E. Forensic Examination http://codeforces.com/problemset/problem/666/E 题目大意:给模式串S以及m个特殊串,q个询问,询问S的子串 ...
随机推荐
- HDFS命令行及JAVA API操作
查看进程 jps 访问hdfs: hadoop-root:50070 hdfs bash命令: hdfs dfs <1> -help: 显示命令的帮助的信息 <2> - ...
- 040 RabbitMq及数据同步02
1.Spring AMQP (1)简介 Spring有很多不同的项目,其中就有对AMQP的支持: Spring AMQP的页面:http://spring.io/projects/spring-amq ...
- malloc,free,calloc,realloc函数
malloc函数 原型:extern void* malloc(unsigned int size): 功能:动态分配内存: 注意:size仅仅为申请内存字节大小,与申请内存块中存储的数据类型无关,故 ...
- scala 函数式编程之集合操作
Scala的集合体系结构 // Scala中的集合体系主要包括:Iterable.Seq.Set.Map.其中Iterable是所有集合trait的根trai.这个结构与Java的集合体系非常相似. ...
- Scala 数组操作之Array、ArrayBuffer以及遍历数组
ArrayBuffer 在Scala中,如果需要类似于Java中的ArrayList这种长度可变的集合类,则可以使用ArrayBuffer. // 如果不想每次都使用全限定名,则可以预先导入Array ...
- 上传文件时用form.submit提交的时候在低版本的IE中报拒绝访问的错误
上传文件的时候,在IE7下总是传不了,但FireFox,IE11和Chrome下则可以上传.发现是form.submit();时出错了(“拒绝访问”). html代码为: <label oncl ...
- delegate、Action、Func的用法
委托的特点 委托类似于 C++ 函数指针,但它们是类型安全的. 委托允许将方法作为参数进行传递. 委托可用于定义回调方法. 委托可以链接在一起. delegate的用法 delegate void B ...
- vs2017添加引用提示“找不到 Microsoft.VisualStudio.Shell.Interop.IVsReferenceManager 服务的实例”解决方案
vs2017添加引用提示“找不到 Microsoft.VisualStudio.Shell.Interop.IVsReferenceManager 服务的实例” 不知道是不是安装时候的问题?解决方法: ...
- 推荐算法之Thompson(汤普森)采样
如果想理解汤普森采样算法,就必须先熟悉了解贝塔分布. 一.Beta(贝塔)分布 Beta分布是一个定义在[0,1]区间上的连续概率分布族,它有两个正值参数,称为形状参数,一般用α和β表示,Beta分布 ...
- dubbo循序渐进 - 什么是RPC
RPC的核心并不在于使用什么协议.RPC的目的是让你在本地调用远程的方法,而对你来说这个调用是透明的,你并不知道这个调用的方法是部署哪里.通过RPC能解耦服务,这才是使用RPC的真正目的.RPC的原理 ...