BZOJ5417: [Noi2018]你的名字
BZOJ5417: [Noi2018]你的名字
https://lydsy.com/JudgeOnline/problem.php?id=5417
分析:
后缀数组 https://www.cnblogs.com/suika/p/9426839.html
- 现在来看写出这样的代码应该不是什么问题了,感觉比\(sa\)好写。
- 先建出\(T\)串的后缀自动机,由于求的是本质不同子串数量,直接在\(T\)串的后缀自动机上统计每个结点的贡献即可。
- 那么我们只需求出每个结点能够向左匹配的长度\(h_i\)
- 答案就是\(\sum\limits_i max(0,len[i]-max(len[fa[i]],h[i]))\)。
- 转化成求\(T\)每个前缀和\(S\)串匹配的后缀长度。
- 这个和正常匹配差不多,有\(ch\)就走,然后在判断是否有一个\(endpos\)出现在特定区间内。
- 如果没有就减少匹配长度并向上跳。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#include <iostream>
using namespace std;
#define N 1000050
typedef long long ll;
char w1[N],w2[N];
int n,m,h[N];
int siz[N*20],ls[N*20],rs[N*20],tot,root[N];
int ke[N],ro[N],L,R;
char buf[100000],*p1,*p2;
#define nc() (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<<2)+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[30];
int top=0;
do{sta[top++]=x%10,x/=10;}while(x);
while(top) push(sta[--top]+'0'); push('\n');
}
void update(int l,int r,int x,int &p) {
if(!p) p=++tot;
siz[p]++;
if(l==r) return ;
int mid=(l+r)>>1;
if(x<=mid) update(l,mid,x,ls[p]);
else update(mid+1,r,x,rs[p]);
}
int merge(int x,int y) {
if(!x||!y) return x+y;
int p=++tot;
ls[p]=merge(ls[x],ls[y]);
rs[p]=merge(rs[x],rs[y]);
siz[p]=siz[ls[p]]+siz[rs[p]];
return p;
}
int OK;
void query(int l,int r,int x,int y,int p) {
if(!p||OK) return ;
if(x<=l&&y>=r) {if(siz[p])OK=1; return ;}
int mid=(l+r)>>1;
if(x<=mid) query(l,mid,x,y,ls[p]);
if(y>mid) query(mid+1,r,x,y,rs[p]);
}
struct sam {
int ch[N][26],len[N<<1],lst,cnt,fa[N<<1],tag[N<<1];
void init() {lst=cnt=1;}
void insert(int x,int id) {
int p=lst,np=++cnt,q,nq;
lst=np; len[np]=len[p]+1; tag[np]=id;
for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
if(!p) fa[np]=1;
else {
q=ch[p][x];
if(len[q]==len[p]+1) fa[np]=q;
else {
nq=++cnt;
len[nq]=len[p]+1; tag[nq]=tag[q];
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];
fa[np]=fa[q]=nq;
for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
}
void kero() {
int i;
for(i=1;i<=cnt;i++) ke[len[i]]++;
for(i=1;i<=cnt;i++) ke[i]+=ke[i-1];
for(i=cnt;i;i--) ro[ke[len[i]]--]=i;
for(i=cnt;i>1;i--) {
int p=ro[i]; root[fa[p]]=merge(root[fa[p]],root[p]);
}
}
void run() {
int i,p=1,now=0;
for(i=1;i<=m;i++) {
int x=w2[i]-'a';
if(ch[p][x]) {
p=ch[p][x]; now++;
}else {
for(;p&&!ch[p][x];p=fa[p]) ;
if(!p) {p=1; now=0; h[i]=0; continue;}
else {
now=len[p]+1; p=ch[p][x];
}
}
if(L!=1||R!=n) {
while(1) {
OK=0;
query(1,n,L+now-1,R,root[p]);
if(OK) break;
now--; if(now==len[fa[p]]) p=fa[p];
if(!p) {
p=1; now=0; break;
}
}
}
h[i]=now;
}
}
void get_ans() {
ll ans=0; int i;
for(i=2;i<=cnt;i++) {
ans+=max(0,len[i]-max(len[fa[i]],h[tag[i]]));
}
write(ans);
for(i=1;i<=cnt;i++) memset(ch[i],0,sizeof(ch[i])),len[i]=tag[i]=fa[i]=0;
}
}S,T;
int main() {
char s=nc();
while(s<'a'||s>'z') s=nc();
S.init();
int i;
for(;s>='a'&&s<='z';s=nc()) {
w1[++n]=s;
}
for(i=1;i<=n;i++) {
S.insert(w1[i]-'a',i);
update(1,n,i,root[S.lst]);
}
S.kero();
int q;
q=rd();
while(q--) {
T.init();
s=nc();
m=0;
while(s<'a'||s>'z') s=nc();
for(;s>='a'&&s<='z';s=nc()) {
w2[++m]=s;
}
L=rd(); R=rd();
for(i=1;i<=m;i++) T.insert(w2[i]-'a',i);
S.run();
T.get_ans();
}
fwrite(pbuf,1,pp-pbuf,stdout);
}