题意

考虑二分答案\(mid\),现在我们要判断\(s[c...c+mid-1]\)是否在\(s[a...b]\)出现过。

首先找到\(s[c...c+mid-1]\)所在的状态:

建出\(parent\ tree\),从\(s[1...c+mid-1]\)的节点(这个可以记录)用倍增向上跳到最后一个\(len\geqslant mid\)的节点即可,记这个节点为\(now\)。

之后我们要判断\(now\)的\(endpos\)中是否含有\([a+mid-1,b]\)中的某个数,我们给每个节点开个权值线段树用来维护该节点\(endpos\)(相当于桶),从\(parent\ tree\)向上合并线段树即可。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=500010;
int n,m,tot,cnt,t;
int id[maxn],root[maxn],head[maxn];
int f[maxn][20];
char s[maxn];
struct edge{int to,nxt;}e[maxn<<1];
struct Seg
{
#define lc(p) (seg[p].lc)
#define rc(p) (seg[p].rc)
#define sum(p) (seg[p].sum)
int lc,rc,sum;
}seg[maxn*60];
inline int read()
{
char c=getchar();int res=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
inline void add_edge(int u,int v)
{
e[++cnt].nxt=head[u];
head[u]=cnt;
e[cnt].to=v;
}
inline void up(int p){sum(p)=sum(lc(p))+sum(rc(p));}
void insert(int &p,int l,int r,int pos)
{
if(!p)p=++tot;
sum(p)++;
if(l==r)return;
int mid=(l+r)>>1;
if(pos<=mid)insert(lc(p),l,mid,pos);
else insert(rc(p),mid+1,r,pos);
}
int query(int p,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr)return sum(p);
int mid=(l+r)>>1,res=0;
if(ql<=mid)res+=query(lc(p),l,mid,ql,qr);
if(qr>mid)res+=query(rc(p),mid+1,r,ql,qr);
return res;
}
int merge(int p,int q,int l,int r)
{
if(!p||!q)return p+q;
int x=++tot,mid=(l+r)>>1;sum(x)=sum(p)+sum(q);
if(l==r)return x;
lc(x)=merge(lc(p),lc(q),l,mid);
rc(x)=merge(rc(p),rc(q),mid+1,r);
return x;
}
struct SAM
{
int tot,last;
int fa[maxn],len[maxn];
int ch[maxn][30];
SAM(){last=tot=1;}
inline void add(int c)
{
int now=++tot;len[now]=len[last]+1;
int p=last;last=now;
while(p&&!ch[p][c])ch[p][c]=now,p=fa[p];
if(!p){fa[now]=1;return;}
int q=ch[p][c];
if(len[q]==len[p]+1)fa[now]=q;
else
{
int nowq=++tot;
len[nowq]=len[p]+1;
memcpy(ch[nowq],ch[q],sizeof(ch[q]));
fa[nowq]=fa[q];fa[q]=fa[now]=nowq;
while(p&&ch[p][c]==q)ch[p][c]=nowq,p=fa[p];
}
}
}sam;
void dfs(int x)
{
for(int i=1;i<=t;i++)f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
f[y][0]=x;dfs(y);
root[x]=merge(root[x],root[y],1,n);
}
}
inline bool check(int mid,int a,int b,int c,int d)
{
int now=id[c+mid-1];
for(int i=t;~i;i--)if(f[now][i]&&sam.len[f[now][i]]>=mid)now=f[now][i];
return query(root[now],1,n,a+mid-1,b)>0;
}
int main()
{
n=read(),m=read();
scanf("%s",s+1);
id[0]=1;
for(int i=1;i<=n;i++)sam.add(s[i]-'a'),id[i]=sam.last,insert(root[sam.last],1,n,i);
for(int i=2;i<=sam.tot;i++)add_edge(sam.fa[i],i);
t=(int)log2(sam.tot)+1;dfs(1);
while(m--)
{
int a=read(),b=read(),c=read(),d=read();
int l=0,r=min(b-a+1,d-c+1),ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid,a,b,c,d))ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}

luoguP4094 [HEOI2016/TJOI2016]字符串的更多相关文章

  1. BZOJ 4556 [HEOI2016/TJOI2016]字符串

    BZOJ 4556 [HEOI2016/TJOI2016]字符串 其实题解更多是用后缀数组+数据结构的做法,貌似也不好写. 反正才学了 sam 貌似比较简单的做法. 还是得先二分,然后倍增跳到 $ s ...

  2. P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案

    $ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...

  3. [HEOI2016/TJOI2016]字符串

    嘟嘟嘟 今天复习一下SAM. lcp固然不好做,干脆直接翻过来变成后缀.首先答案一定满足单调性,所以我们二分lcp的长度\(mid\),然后判断\(s[d \ldots d + mid - 1]\)是 ...

  4. [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)

    后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...

  5. 【[HEOI2016/TJOI2016]字符串】

    码农题啊 上来先无脑一个\(SA\)的板子,求出\(SA\)和\(het\)数组 我们只需要从\(sa[i]\in[a,b]\)的所有\(i\)中找到一个\(i\)使得\(sa[i]\)和\(rk[c ...

  6. BZOJ4556 HEOI2016/TJOI2016字符串 (后缀树+主席树)

    二分答案后相当于判断一个区间的后缀与某个后缀的最长公共前缀是否能>=ans.建出后缀树,在上述问题中后者所在节点向上倍增的跳至len>=ans的最高点,然后相当于查询子树中是否有该区间的节 ...

  7. HEOI2016/TJOI2016 字符串问题

    题目链接:戳我 非常不好意思,因为想要排版,所以今天先只把代码贴出来,明天补题解. 40pts暴力:直接暴力匹配 #include<iostream> #include<cstrin ...

  8. 洛谷 P4094 [HEOI2016/TJOI2016]字符串(SA+主席树)

    题面传送门 一道码农题---- u1s1 感觉这类题目都挺套路的,就挑个有代表性的题写一篇题解罢. 首先注意到答案满足可二分性,故考虑二分答案 \(mid\),转化为判定性问题. 考虑怎样检验 \(m ...

  9. cdq分治(hdu 5618 Jam's problem again[陌上花开]、CQOI 2011 动态逆序对、hdu 4742 Pinball Game、hdu 4456 Crowd、[HEOI2016/TJOI2016]序列、[NOI2007]货币兑换 )

    hdu 5618 Jam's problem again #include <bits/stdc++.h> #define MAXN 100010 using namespace std; ...

随机推荐

  1. 央行辟谣未发行“DC/EP”和“DCEP” 法定数字货币仍在测试阶段

    http://www.sohu.com/a/354709423_100157595 近期,中国央行再度就法定数字货币发布公告,指出目前系统仍处于研究测试过程中,市场上交易“DC/EP”或“DCEP”均 ...

  2. As Simple as One and Two

    time limit per test3 secondsmemory limit per test256 megabytesinput: standard inputoutput: standard ...

  3. windows10安装最新的redis

    官方给的redis的windows版本最新为3,而linux版本是5 这里通过win10子系统安装,win10子系统的配置见另一篇博客https://www.cnblogs.com/MC-Curry/ ...

  4. 去掉Oracle数据库中字段值前后空格

    发现oracle字段钟包含了空格,可能是前空格,也可能是后空格,因为是历史数据,需要特殊处理.但是中间的空格可能是一些特殊人员的姓名含有,这种情况不能处理.所以我们只需要处理前后空格即可.使用如下sq ...

  5. Codeforces Round 596 题解

    万幸的是终于碰上了一场上分好场. 不幸的是一开始差点不会 A. 万幸的是想了个不那么稳的结论过了 pretest. 不幸的是罚时很高,而且慌得一比. 万幸的是然后半个小时内把 B 和 C 码了. 不幸 ...

  6. [题解向] PAM简单习题

    \(1\) LG5496 [模板]回文自动机 对于 \(s\) 的每个位置,请求出以该位置结尾的回文子串个数. \(|s|\leq 1e6\) 然后就是PAM的板子题咋感觉好像没有不是很板的PAM题呢 ...

  7. CentOS7下rsync服务端与Windows下cwRsync客户端实现数据同步配置方法

    最近需求想定期备份服务器d盘的数据到Linux服务器上面,做个笔记顺便写下遇到的问题 以前整过一个win下的cwrsync(客户端)+rsync(服务端:存储)的bat脚本 和整过一个Linux下的r ...

  8. c#汉字转拼音首字母全拼支持多音字

    1.首先在NuGet安装pingyinConverter 2.下载-安装-引用ChineseChar.dll到项目中 官网了解:http://www.microsoft.com/zh-cn/downl ...

  9. 微信小程序反编译

    看到一个有意思的小程序,想了解是如何实现的,于是找了反编译方法. 安装adb驱动 百度安装adb驱动, 设计设置开发者模式,连接电脑. -> % adb devices List of devi ...

  10. Linux中vim编辑命令

    vim 功能 : 一个强大的文本编辑器   语法格式 :vim [ 选项 ] / 路径 / 文本文件名 命令格式: vi [ 选项 ] [ 文件名 ]   +num 打开某个文件直接跳转到 num 行 ...