码农题啊

上来先无脑一个\(SA\)的板子,求出\(SA\)和\(het\)数组

我们只需要从\(sa[i]\in[a,b]\)的所有\(i\)中找到一个\(i\)使得\(sa[i]\)和\(rk[c]\)之间的最小值最大就好了

但是还必须得满足\(sa[i]+lcp-1<=b\),毕竟整个串还得在\([a,b]\)内部

考虑一下二分答案

根据\(het\)数组的性质显然越靠近\(rk[c]\)的\(sa[i]\)形成的\(lcp\)越长,于是我们可以利用一个\(ST\)表加二分找到从\(rk[i]\)往前开始的一个尽量长的区间整个区间内的所有\(het\)大于等于当前二分出来的\(mid\)

之后查一下这个区间内部有多少个在\([a,b-mid+1]\)范围的\(sa\),这样如果存在任意一个数,就会满足条件

可以用主席树来做到这一点

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define re register
#define LL long long
#define maxn 100005
#define M 5000005
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
re char c=getchar();int x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int n,m,Q,cnt;
char S[maxn];
int rk[maxn],het[maxn],sa[maxn],tax[maxn],tp[maxn],rt[maxn],log_2[maxn];
int St[maxn][18];
int ls[M],rs[M],d[M];
int change(int pre,int x,int y,int pos)
{
int root=++cnt;
d[root]=d[pre]+1;
if(x==y) return root;
ls[root]=ls[pre],rs[root]=rs[pre];
int mid=x+y>>1;
if(pos<=mid) ls[root]=change(ls[pre],x,mid,pos);
else rs[root]=change(rs[pre],mid+1,y,pos);
return root;
}
inline void qsort()
{
for(re int i=0;i<=m;i++) tax[i]=0;
for(re int i=1;i<=n;i++) tax[rk[i]]++;
for(re int i=1;i<=m;i++) tax[i]+=tax[i-1];
for(re int i=n;i;--i) sa[tax[rk[tp[i]]]--]=tp[i];
}
inline int ask(int l,int r)
{
int k=log_2[r-l+1];
return min(St[l][k],St[r-(1<<k)+1][k]);
}
int query(int p1,int p2,int pos,int x,int y)
{
if(x==y) return d[p2]-d[p1];
int mid=x+y>>1;
if(pos<=mid) return query(ls[p1],ls[p2],pos,x,mid);
return d[ls[p2]]-d[ls[p1]]+query(rs[p1],rs[p2],pos,mid+1,y);
}
inline int check(int now,int l,int r,int pos)
{
int L=1,R=pos,to=0;
while(L<=R)
{
int mid=L+R>>1;
if(ask(mid,pos)>=now) R=mid-1,to=mid;
else L=mid+1;
}
if(to&&query(rt[to-2],rt[pos],r-now+1,1,n)-((l-1)?(query(rt[to-2],rt[pos],l-1,1,n)):0)) return 1;
L=pos+1,R=n,to=0;
while(L<=R)
{
int mid=L+R>>1;
if(ask(pos+1,mid)>=now) L=mid+1,to=mid;
else R=mid-1;
}
if(to&&query(rt[pos-1],rt[to],r-now+1,1,n)-((l-1)?(query(rt[pos-1],rt[to],l-1,1,n)):0)) return 1;
return 0;
}
int main()
{
n=read(),Q=read(),scanf("%s",S+1);
m=75;
for(re int i=1;i<=n;i++)
rk[i]=S[i]-'a'+1,tp[i]=i;
qsort();
for(re int w=1,p=0;p<n;m=p,w<<=1)
{
p=0;
for(re int i=1;i<=w;i++) tp[++p]=n-w+i;
for(re int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
qsort();
for(re int i=1;i<=n;i++) std::swap(rk[i],tp[i]);
rk[sa[1]]=p=1;
for(re int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
}
int k=0;
for(re int i=1;i<=n;i++)
{
if(k) --k;
int j=sa[rk[i]-1];
while(S[i+k]==S[j+k]) ++k;
het[rk[i]]=k;
}
memset(St,20,sizeof(St));
int num=0;
for(re int i=2;i<=n;i++) log_2[i]=1+log_2[i/2];
for(re int i=1;i<=n;i++) St[i][0]=het[i];
for(re int j=1;j<=log_2[n];j++)
for(re int i=1;i+(1<<j)-1<=n;i++) St[i][j]=min(St[i][j-1],St[i+(1<<(j-1))][j-1]);
for(re int i=1;i<=n;i++)
rt[i]=change(rt[i-1],1,n,sa[i]);
while(Q--)
{
int x=read(),y=read(),xx=read(),yy=read();
int R=y-x+1,L=1,ans=0;
while(L<=R)
{
int mid=L+R>>1;
if(check(mid,x,y,rk[xx])) L=mid+1,ans=mid;
else R=mid-1;
}
ans=min(ans,yy-xx+1);
printf("%d\n",ans);
}
return 0;
}

【[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. luoguP4094 [HEOI2016/TJOI2016]字符串

    题意 考虑二分答案\(mid\),现在我们要判断\(s[c...c+mid-1]\)是否在\(s[a...b]\)出现过. 首先找到\(s[c...c+mid-1]\)所在的状态: 建出\(paren ...

  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. [转] 常用的sql server规范

      常见的字段类型选择 1.字符类型建议采用varchar/nvarchar数据类型 2.金额货币建议采用money数据类型 3.科学计数建议采用numeric数据类型 4.自增长标识建议采用bigi ...

  2. PHP反射ReflectionClass、ReflectionMethod 学习笔记 (一)

    PHP5 具有完整的反射API,添加对类.接口.函数.方法和扩展进行反向工程的能力. 反射是什么? 它是指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类.方法.属性.参数等的详细信息,包括 ...

  3. 如鹏网学习笔记(七)HTML基础

    HTML笔记 一.HTML简介 1,HTML (Hyper Text Mark-up Language) 超文本标记语言,是一种编程语言,也可以说是一种标准.规范. 2,HTML提供了一系列标记(标签 ...

  4. 面向对象(基础oop)之继承总结

    大家好,我叫李京阳,,很高兴认识大家,之所以我想开一个自己的博客,就是来把自己所了解的知识点通过自己的话写一下,希望被博客园的朋友们点评和一起讨论一下,也希望从博客园中多认识一些软件开发人员!现在我开 ...

  5. CXF开发WebService

    CXF开发Web Service 参考链接 使用 spring 框架来集成 Web Services 开发 浏览器调用接口 大概这样, 没成功 加@WebMethod(action="get ...

  6. Spring Boot学习笔记(二)全局捕获异常处理

    非常简单只需要创建自己的异常处理类,加上两个注解,就可以了

  7. uwsgi/uWSGI/WSGI简介

    参考文章 uWSGI是一个Web服务器,它实现了WSGI协议.uwsgi.http等协议.Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换.z WSGI是一种Web服务器网 ...

  8. MyBatis缓存通俗易懂

    1.1     mybatis缓存介绍 如下图,是mybatis一级缓存和二级缓存的区别图解: Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的 ...

  9. Redis实现分布式锁1

    Jedis结合setNX方法实现分布式锁 public boolean lock(String key, int exprie) { try { exprie = exprie <= 0 ? 6 ...

  10. DOM3 textInput事件

    DOM3中引入了文本事件,其中之一 textInput . 当用户再可编辑区域输入字符时触发该事件. 与keypress不同的是,该事件只会在用户输入可视字符时触发,而keypres事件则只要按下键即 ...