LOJ_#2720. 「NOI2018」你的名字 _后缀数组+主席树+倍增
题面:
考虑枚举T串的每个后缀i,我们要做两件事。
一、统计有多少子串[i,j]在S中要求位置出现。
二、去重。
第二步好做,相当于在后缀数组上找到后继,假设后继的height为p。
那对于i这个后缀,只计算i+p后面的即可。
一的话每次找到最远的能匹配的一个位置,可以发现枚举每个后缀时右端点单调,双指针扫一下。
剩下的就是快速的判定一个子串是否合法。
考虑在后缀数组上左右二分到一个区间满足所有后缀和i的lcp都大于等于len(len=j-i+1)。
判断这段区间是否存在一个位置,刚好在S中[l,r-len+1]的位置上。主席树直接做。
重复一下做法:
对所有的串建立后缀数组,同时求出height。
在所有S串的地方主席树上更新。
双指针扫一遍,check的时候左右二分找区间,主席树上查即可。
这里补充一个技巧...因为区间可能不会很长,所以用倍增会比直接二分快很多....
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define N 1700000
#define M 100050
#define _max(x,y) ((x)>(y)?(x):(y))
#define _min(x,y) ((x)<(y)?(x):(y))
typedef long long ll;
int n,Q,r[N],ln,be[M],ed[M],m;
int wa[N],wb[N],ws[N],wv[N],sa[N],Rank[N],height[N],ql[M],qr[M],ppp[N],pos[N],lst[M];
int f[22][N],Lg[N],t[N*10],ls[N*10],rs[N*10],root[N],cnt;
inline char nc() {
static char buf[100000],*p1,*p2;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
int rd() {
int x=0; char s=nc();
while(s<'0'||s>'9') s=nc();
while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc();
return x;
}
char pbuf[100000],*pp=pbuf;
void push(const char c) {
if(pp-pbuf==100000) fwrite(pbuf,1,100000,stdout),pp=pbuf;
*pp++=c;
}
void write(ll x) {
static int sta[70];
int top=0;
do{sta[top++]=x%10,x/=10;}while(x);
while(top) push(sta[--top]+'0');
}
void build_suffix_array() {
int i,j,p,*x=wa,*y=wb,*t;
m=32;
for(i=0;i<m;i++) ws[i]=0;
for(i=0;i<n;i++) ws[x[i]=r[i]]++;
for(i=1;i<m;i++) ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
for(p=j=1;p<n;j<<=1,m=p) {
for(p=0,i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]-j>=0) y[p++]=sa[i]-j;
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<m;i++) ws[i]=0;
for(i=0;i<n;i++) ws[wv[i]]++;
for(i=1;i<m;i++) ws[i]+=ws[i-1];
for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,i=p=1,x[sa[0]]=0;i<n;i++) {
if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]) x[sa[i]]=p-1;
else x[sa[i]]=p++;
}
}
for(i=1;i<n;i++) Rank[sa[i]]=i;
for(i=p=0;i<n-1;height[Rank[i++]]=p)
for(p?p--:0,j=sa[Rank[i]-1];r[i+p]==r[j+p];p++);
for(Lg[0]=-1,i=1;i<n;i++) f[0][i]=height[i],Lg[i]=Lg[i>>1]+1;
for(i=1;(1<<i)<=n;i++) {
for(j=1;j+(1<<i)-1<n;j++) {
f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
}
}
}
int get_min(int x,int y) {
int len=Lg[y-x+1],a=f[len][x],b=f[len][y-(1<<len)+1];
return _min(a,b);
}
int lcp(int x,int y) {
x=Rank[x],y=Rank[y];
if(x>y) swap(x,y);
return get_min(x+1,y);
}
void insert(int l,int r,int v,int x,int &y) {
y=++cnt; t[y]=t[x]+1;
if(l==r) return ;
int mid=(l+r)>>1;
if(v<=mid) rs[y]=rs[x],insert(l,mid,v,ls[x],ls[y]);
else ls[y]=ls[x],insert(mid+1,r,v,rs[x],rs[y]);
}
int Qx,Qy,ok;
void query(int l,int r,int x,int y) {
if(t[x]==t[y]||ok) return ;
if(Qx<=l&&Qy>=r) {ok=1; return ;}
int mid=(l+r)>>1;
if(Qx<=mid) query(l,mid,ls[x],ls[y]);
if(Qy>mid) query(mid+1,r,rs[x],rs[y]);
}
bool check(int i,int j,int q) {
int len=j-i+1; Qx=ql[q],Qy=qr[q];
if(len>Qy-Qx+1) return 0;
int p=Rank[i];
int L=p,R=p,d;
for(d=1;p>d&&get_min(p-d+1,p)>=len;d<<=1);
for(d>>=1;d;d>>=1) if(get_min(L-d+1,p)>=len) L-=d;
for(d=1;p+d<n&&get_min(p+1,p+d)>=len;d<<=1);
for(d>>=1;d;d>>=1) if(get_min(p+1,R+d)>=len) R+=d;
Qy=Qy-len+1;
ok=0;
query(0,ln-1,root[L-1],root[R]);
return ok;
}
int main() {
char s=nc();
while(s<'a'||s>'z') s=nc();
register int i,j;
for(;s>='a'&&s<='z';s=nc()) {
r[ln++]=s-'a'+1;
}
n=ln; r[n++]=27;
Q=rd();
for(i=1;i<=Q;i++) {
be[i]=n;
s=nc();
while(s<'a'||s>'z') s=nc();
for(;s>='a'&&s<='z';s=nc()) {
pos[n]=i; r[n++]=s-'a'+1;
}
ed[i]=n-1;
ql[i]=rd()-1; qr[i]=rd()-1;
if(i!=Q) r[n++]=27;
}
r[n++]=0;
build_suffix_array();
for(i=n-1;i>0;i--) {
int p=sa[i],u=pos[p];
if(!u) continue;
if(lst[u]) ppp[p]=lcp(p,lst[u]);
lst[u]=p;
}
for(i=1;i<n;i++) {
int p=sa[i];
root[i]=root[i-1];
if(p>=0&&p<ln) insert(0,ln-1,p,root[i],root[i]);
}
for(i=1;i<=Q;i++) {
ll ans=0;
int k=be[i];
for(j=be[i];j<=ed[i];j++) {
if(k<j) k=j;
while(k<=ed[i]&&check(j,k,i)) k++;
k--;
ans+=ed[i]-j+1-_max(k-j+1,ppp[j]);
}
write(ans); push('\n');
}
fwrite(pbuf,1,pp-pbuf,stdout);
}
LOJ_#2720. 「NOI2018」你的名字 _后缀数组+主席树+倍增的更多相关文章
- LOJ 2720 「NOI2018」你的名字——后缀自动机
题目:https://loj.ac/problem/2720 自己总是分不清 “SAM上一个点的 len[ ] ” 和 “一个串的前缀在 SAM 上匹配的 len ”. 于是原本想的 68 分做法是, ...
- loj#2720. 「NOI2018」你的名字
链接大合集: loj uoj luogu bzoj 单纯地纪念一下写的第一份5K代码.../躺尸 因为ZJOI都不会所以只好写NOI的题了... 总之字符串题肯定一上来就拼个大字符串跑后缀数组啦! ( ...
- 【LOJ】#2720. 「NOI2018」你的名字
题解 把S串建一个后缀自动机 用一个可持久化权值线段树维护每个节点的right集合是哪些节点 求本质不同的子串我们就是要求T串中以每个点为结束点的串有多少在\(S[l..r]\)中出现过 首先我们需要 ...
- 「NOI2018」你的名字
「NOI2018」你的名字 题目描述 小A 被选为了\(ION2018\) 的出题人,他精心准备了一道质量十分高的题目,且已经 把除了题目命名以外的工作都做好了. 由于\(ION\) 已经举办了很多届 ...
- Luogu4770 NOI2018你的名字(后缀数组+线段树)
即求b串有多少个本质不同的非空子串,在a串的给定区间内未出现.即使已经8102年并且马上就9102年了,还是要高举SA伟大旗帜不动摇. 考虑离线,将所有询问串及一开始给的串加分隔符连起来,求出SA.对 ...
- 「NOI2018」屠龙勇士(EXCRT)
「NOI2018」屠龙勇士(EXCRT) 终于把传说中 \(NOI2018D2\) 的签到题写掉了... 开始我还没读懂题目...而且这题细节巨麻烦...(可能对我而言) 首先我们要转换一下,每次的 ...
- LOJ #2721. 「NOI2018」屠龙勇士(set + exgcd)
题意 LOJ #2721. 「NOI2018」屠龙勇士 题解 首先假设每条龙都可以打死,每次拿到的剑攻击力为 \(ATK\) . 这个需要支持每次插入一个数,查找比一个 \(\le\) 数最大的数(或 ...
- loj#2718. 「NOI2018」归程
题目链接 loj#2718. 「NOI2018」归程 题解 按照高度做克鲁斯卡尔重构树 那么对于询问倍增找到当前点能到达的高度最小可行点,该点的子树就是能到达的联通快,维护子树中到1节点的最短距离 s ...
- loj#2721. 「NOI2018」屠龙勇士
题目链接 loj#2721. 「NOI2018」屠龙勇士 题解 首先可以列出线性方程组 方程组转化为在模p意义下的同余方程 因为不保证pp 互素,考虑扩展中国剩余定理合并 方程组是带系数的,我们要做的 ...
随机推荐
- 仿照ArrayList自己生成的MyList对象
现在需要自己生成一个list集合,基本雷同ArrayList,不使用API的List接口. 实现如下: MyList的代码: public class MyList<T> { privat ...
- A/B测试与灰度发布
1.A/B测试与灰度发布的理论 产品是多维度的,设计体验.交互体验.系统质量.运营支持等等, 测试的目的是为了系统最终的交付,一套各方面都足够好的系统,而不是文档上定义的系统,系统是需要不断进化的. ...
- WinForm开发----关闭window窗体最好的办法
最近有一人问道,如何切换窗体.一想到这,我就想,不就是new一个form,然后就show么? 可是我发现,当你控制某个属性的时候,不是不能控制,只是很麻烦而已.有没有好的办法?当然有,咋办? 最简单最 ...
- ubuntu16.04下hive安装与配置
Hive是什么? 由Facebook开源用于解决海量 结构化日志的数据统计: Hive是基于Hadoop的一个 数据仓库工具,可以将结构化的数据文件映射 成一张表,并提供类SQL查询功能: 构建在Ha ...
- SpringBoot学习笔记(7):Druid使用心得
SpringBoot学习笔记(7):Druid使用心得 快速开始 添加依赖 <dependency> <groupId>com.alibaba</groupId> ...
- static_func
<?php function testing() { static $a = 1; $a *= 2; echo $a."\n"; } testing(); testing() ...
- iOS base64编码 MD5 加密
//创建一个Base64编码的NSString对象 //字符串 转二进制 NSData *nsdata = [@"iOS Developer Tips encoded in Base64&q ...
- Data Structure Graph: prim
最小生成树算法.这里的s是可以随意选取的,不影响树的生成,但是不同的s有不同的dis #include <iostream> #include <vector> #includ ...
- js之语句的一些需要注意的事情
1.delete运算符是用来删除一个对象的 属性,但有一点需要注意:使用var声明的变量虽为全局变量,单不是全局对象的属性,不可以用delete删除,而不用var直接声明的全局变量而直接赋值的为全局对 ...
- 【leetcode刷题笔记】Linked List Cycle
Given a linked list, determine if it has a cycle in it. Follow up:Can you solve it without using ext ...