CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增
题意:
给定一个串 $S$ 和若干个串 $T_{i}$
每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号.
数据范围: 需要离线
题解:
首先,很常规的对 $T_{1}$ 到 $T_{rmax}$ 的所有字符串构建一个广义后缀自动机.
来一遍线段树合并,合并的权值是每个 $T$ 串出现的次数.
合并完毕后,再广义后缀自动机上的每个点的线段树上都能查到有哪些串能覆盖当前串.
把询问按照右端点排序,将 $S$ 匹配到广义后缀自动机当中,并通过倍增来将每个询问串进行定位..
最后 DFS 一遍算出答案即可.
还是很优美 + 复杂的.
Code:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 2000000
#define N 30
#define sigma 26
#define inf 1000000000 using namespace std; vector <int> G[maxn];
char str[maxn],ss[maxn];
int str_len,m;
int ss_len[maxn];
int fa[maxn][27];
int ans1[maxn],ans2[maxn]; struct Query{ int l,r,ans,pl,pr,id; }arr[maxn];
int cmp(Query a,Query b) { return a.pr<b.pr; } namespace Seg{
struct Segment_Tree{ int l,r,maxv; }node[maxn<<2];
int root[maxn],nodes;
int newnode(){return ++nodes; }
void update(int p,int l,int r,int &o){
if(!o) o=newnode();
if(l==r) { node[o].maxv+=1; return ; }
int mid=(l+r)>>1;
if(p<=mid) update(p,l,mid,node[o].l);
else update(p,mid+1,r,node[o].r);
node[o].maxv=max(node[node[o].l].maxv,node[node[o].r].maxv);
}
int merge(int x,int y,int l,int r){
if(!x||!y) return x+y;
int o=newnode(),mid=(l+r)>>1;
if(l==r) {
node[o].maxv=node[x].maxv+node[y].maxv;
return o;
}
node[o].l=merge(node[x].l,node[y].l,l,mid);
node[o].r=merge(node[x].r,node[y].r,mid+1,r);
node[o].maxv=max(node[node[o].l].maxv,node[node[o].r].maxv);
return o;
}
int query_max(int l,int r,int L,int R,int o) {
if(!o||l>r||r<L||l>R) return 0;
if(l>=L&&r<=R) return node[o].maxv;
int mid=(l+r)>>1,res=0;
res=max(res,query_max(l,mid,L,R,node[o].l));
res=max(res,query_max(mid+1,r,L,R,node[o].r));
return res;
}
int query_pos(int p,int l,int r,int L,int R,int o){
if(l>r||r<L||l>R||!o||node[o].maxv<p) return inf;
if(l==r) return l;
int mid=(l+r)>>1,a;
if((a=query_pos(p,l,mid,L,R,node[o].l))!=inf) return a;
else return query_pos(p,mid+1,r,L,R,node[o].r);
}
};
namespace SAM{
int f[maxn],dis[maxn],ch[maxn][N],tot,last;
int C[maxn],rk[maxn];
inline void init(){last=++tot; }
void ins(int c){
int p=last,np,nq;
if(ch[p][c]){
int q=ch[p][c];
if(dis[q]==dis[p]+1) last=q;
else
{
nq=++tot,last=nq;
f[nq]=f[q],f[q]=nq,dis[nq]=dis[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][c]==q)ch[p][c]=nq,p=f[p];
}
}
else {
np=++tot,last=np,dis[np]=dis[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(dis[q]==dis[p]+1) f[np]=q;
else
{
nq=++tot;
f[nq]=f[q],f[q]=f[np]=nq,dis[nq]=dis[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p];
}
}
}
}
}; int head[maxn],to[maxn],nex[maxn],edges;
void addedge(int u,int v) { nex[++edges]=head[u],head[u]=edges,to[edges]=v; }
void DFS(int u) {
for(int v=head[u];v;v=nex[v])
{
DFS(to[v]);
Seg::root[u]=Seg::merge(Seg::root[u],Seg::root[to[v]],1,m);
}
for(int i=0;i<G[u].size();++i) {
int cur=G[u][i];
int a=Seg::query_max(1,m,arr[cur].l,arr[cur].r,Seg::root[u]);
ans1[arr[cur].id] = a;
if(!a) { ans2[arr[cur].id]=arr[cur].l; continue; }
int b=Seg::query_pos(a,1,m,arr[cur].l,arr[cur].r,Seg::root[u]);
ans2[arr[cur].id] = (b==inf)?0:b;
}
} int main(){
//setIO("input");
//==============================================================================
scanf("%s",str+1),str_len = strlen(str + 1);
SAM::init();
for(int i=1;i<=str_len;++i) SAM::ins(str[i]-'a');
int i,j,queries;
for(SAM::init(),scanf("%d",&m),i=1;i<=m;++i)
{
scanf("%s",ss+1),ss_len[i] = strlen(ss+1);
for(SAM::last=1,j=1;j<=ss_len[i];++j)
{
SAM::ins(ss[j]-'a');
Seg::update(i,1,m,Seg::root[SAM::last]);
}
}
for(i=2;i<=SAM::tot;++i) addedge(SAM::f[i],i),fa[i][0]=SAM::f[i];
for(i=1;i<sigma;++i)
for(j=2;j<=SAM::tot;++j) fa[j][i]=fa[fa[j][i-1]][i-1];
for(scanf("%d",&queries),i=1;i<=queries;++i)
scanf("%d%d%d%d",&arr[i].l,&arr[i].r,&arr[i].pl,&arr[i].pr),arr[i].id=i;
sort(arr+1,arr+1+queries,cmp);
//=================================================================依次处理每个询问
int cur=1,p=1,q;
for(i=1;i<=queries;++i) {
for(;cur<=arr[i].pr;++cur) {
int c=str[cur]-'a';
while(p && !SAM::ch[p][c]) p=SAM::f[p];
if(!p) p=1;
else p=SAM::ch[p][c];
}
q=p;
if(SAM::dis[p]>=(arr[i].pr-arr[i].pl+1)) {
for(j=24;j>=0;--j) {
if(SAM::dis[fa[p][j]] >= (arr[i].pr-arr[i].pl+1)) p=fa[p][j];
}
G[p].push_back(i);
}
p=q;
}
DFS(1);
for(i=1;i<=queries;++i) printf("%d %d\n",ans2[i],ans1[i]);
return 0;
}
CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增的更多相关文章
- 【CF666E】Forensic Examination(后缀自动机,线段树合并)
[CF666E]Forensic Examination(后缀自动机,线段树合并) 题面 洛谷 CF 翻译: 给定一个串\(S\)和若干个串\(T_i\) 每次询问\(S[pl..pr]\)在\(T_ ...
- CF666E Forensic Examination(后缀自动机+动态线段树)
题意 给你一个串 $S$ 以及一个字符串数组 $T[1..m]$ , $q$ 次询问,每次问 $S$ 的子串 $S[p_l..p_r]$ 在 $T[l..r]$ 中的哪个串里的出现次数最多,并输出出现 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- 【BZOJ3413】匹配(后缀自动机,线段树合并)
[BZOJ3413]匹配(后缀自动机,线段树合并) 题面 BZOJ 题解 很好的一道题目. 做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和. 首先是空前缀的出现次数,意味着你 ...
- 【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并
[CF666E]Forensic Examination 题意:给你一个字符串s和一个字符串集合$\{t_i\}$.有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l ...
- 【CF666E】Forensic Examination - 广义后缀自动机+线段树合并
广义SAM专题的最后一题了……呼 题意: 给出一个长度为$n$的串$S$和$m$个串$T_{1\cdots m}$,给出$q$个询问$l,r,pl,pr$,询问$S[pl\cdots pr]$在$T_ ...
- CF 666E Forensic Examination——广义后缀自动机+线段树合并
题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...
- 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并
题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...
- [CF666E]Forensic Examination:后缀自动机+线段树合并
分析 用到了两个小套路: 使用线段树合并维护广义后缀自动机的\(right\)集合. 查询\(S[L,R]\)在\(T\)中的出现次数:给\(T\)建SAM,在上面跑\(S\),跑到\(R\)的时候先 ...
随机推荐
- JDBC的总结
JDBC归纳: DriverManger:驱动管理器类 要操作数据库,必须先与数据库创建连接,得到连接对象 public static Connection getConnection(String ...
- bootstrap-导航条
默认样式的导航条 导航条是在您的应用或网站中作为导航页头的响应式基础组件.它们在移动设备上可以折叠(并且可开可关),且在视口(viewport)宽度增加时逐渐变为水平展开模式. 两端对齐的导航条导航链 ...
- java 文件下载遇到的数个坑
文件的下载在web开发中应该是很常用的功能,近期项目中遇到的一个需求是:前端提供 查询条件以及查询结果的字段,后端拿到这些参数之后,在数据库中根据业务逻辑查询得出查询结果,导出成excel文件,同时传 ...
- Codeforces Round #493 (Div. 2) B. Cutting 前缀和优化_动归水题
不解释,题目过水 Code: #include<cstdio> #include<cmath> #include<algorithm> using namespac ...
- vuex存储和本地存储的区别
1.实质的区别 vuex存的是状态,存储在内存,localstorage是浏览器提供的接口,让你存的是文件,以文件的形式存储在本地 2.应用场景 vuex用于组件之间的传值,localstorage则 ...
- maven的pom.xml配置json依赖
<dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</art ...
- [BOI2007]摩基亚
题目:洛谷P4390.BZOJ1176. 题目大意: 给你一个\(W\times W\)的矩阵,初始每个数都为\(S\).现在有若干操作: 1. 给某个格子加上一个值:2. 询问某个子矩阵的值的和:3 ...
- HDU 5763 Another Meaning (KMP/哈希+DP)
题目大意:给你两个串,一长一短,如果长串中某个子串和短串完全相同,则这个子串可以被替换成"#",求长串所有的表达形式....... 比如"hehehehe"和& ...
- Python基本类型操作
# str = "2017.1.1.wmv" # #print(str[str.rfind('.'):]) # #print(str.count(".")) # ...
- STM32 Cubemx 输出可调频率与占空比的PWM
这里就不对STM32的PWM进行讲解了,想要了解的可以百度一下,这里主要说怎么实现. 1.建立工程,我选的是STM32F103zet6芯片,选择定时器的PWM功能 2.配置时钟,我这里配的是内部时钟, ...