luogu P4770 [NOI2018] 你的名字
题面传送门
先来考虑\(l=1,r=|S|\)怎么做。
首先显然可以先跑个SA数数本质不同子串个数。
然后再对原串建一个SAM,容易发现就是将询问串在原串上面跑匹配,匹配完了以后就可以计算以当前节点为左端点有多少个右端点是不重复且在主串没出现过的。
然后如果询问变成一段区间了那么就线段树合并维护一下endpos集合之后判断走不走即可。
时间复杂度\(O(n\log n)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N (500000+5)
#define M (1000000+5)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;vector<int> G[M];
int n,m,k,Q,T,x,y,z,L,R,H[M],rk[M],sa[M],Ro[M];char A[N],B[M];ll Ans;
struct Ques{int x,y,id;}S[M];I bool cmp(Ques x,Ques y){return x.x^y.x?x.x<y.x:x.y<y.y;}
namespace Tree{
int cnt,L[M*60],R[M*60],F[M*60];I void Ins(int x,int y,int &now,int l=1,int r=n){!now&&(now=++cnt);F[now]=min(F[now],y);if(l==r) return;int m=l+r>>1;x<=m?Ins(x,y,L[now],l,m):Ins(x,y,R[now],m+1,r);}
I int Qry(int x,int y,int now,int l=1,int r=n){if(x>y) return 1e9;if((x<=l&&r<=y)||!now) return F[now];int m=l+r>>1,F1=1e9,F2=1e9;x<=m&&(F1=Qry(x,y,L[now],l,m));y>m&&(F2=Qry(x,y,R[now],m+1,r));return min(F1,F2);}
I int ME(int x,int y){if(!x||!y) return x|y;int Tp=++cnt;F[Tp]=min(F[x],F[y]);L[Tp]=ME(L[x],L[y]);R[Tp]=ME(R[x],R[y]);return Tp;}
}
namespace SAM{
int cnt=1,p,q,La=1,son[M][26],Lk[M],Le[M];I void Ins(int x){
p=La;Le[++cnt]=Le[La]+1;La=cnt;while(p&&!son[p][x]) son[p][x]=La,p=Lk[p];if(!p) {Lk[La]=1;return;}q=son[p][x];if(Le[q]==Le[p]+1){Lk[La]=q;return;}
Le[++cnt]=Le[p]+1;Lk[cnt]=Lk[q];Mc(son[cnt],son[q]);Lk[q]=Lk[La]=cnt;while(p&&son[p][x]==q) son[p][x]=cnt,p=Lk[p];
}I void dfs(int x){for(RI i:G[x]) dfs(i),Ro[x]=Tree::ME(Ro[x],Ro[i]);}I void BD(){for(RI i=2;i<=cnt;i++) G[Lk[i]].PB(i);dfs(1);}
}
I void Solve(){
RI i,j;scanf("%s%d%d",B+1,&L,&R);T=strlen(B+1);for(i=1;i<=T;i++) H[i]=0,S[i]=(Ques){B[i],0,i};sort(S+1,S+T+1,cmp);for(i=1;i<=T;i++)rk[S[i].id]=rk[S[i-1].id]+(S[i].x^S[i-1].x||S[i].y^S[i-1].y);
Ans=1ll*T*(T+1)/2;for(i=1;;i<<=1){
for(j=1;j<=T;j++) S[j]=(Ques){rk[j],i+j<=T?rk[i+j]:0,j};sort(S+1,S+T+1,cmp);for(j=1;j<=T;j++) rk[S[j].id]=rk[S[j-1].id]+(S[j].x^S[j-1].x||S[j].y^S[j-1].y);if(rk[S[T].id]==T) break;
}for(i=1;i<=T;i++) sa[rk[i]]=i;for(i=1;i<=T;i++) {if(rk[i]==1) continue;H[i]=max(H[i-1]-1,0);while(B[i+H[i]]==B[sa[rk[i]-1]+H[i]]) H[i]++;Ans-=H[i];}x=1;y=0;
for(i=T;i;i--){while((z=Tree::Qry(L,R-y+1,Ro[SAM::son[x][B[i]-'a']]))>R&&x^1) x=SAM::Lk[x],y=SAM::Le[x];/*cerr<<y<<' '<<z<<' '<<i<<' '<<x<<'\n';*/z<=R&&(x=SAM::son[x][B[i]-'a'],y++);Ans-=max(min(y,R-z+1)-H[i],0);}printf("%lld\n",Ans);
}
int main(){
freopen("1.in","r",stdin);freopen("1.out","w",stdout);
RI i;Me(Tree::F,0x3f);scanf("%s",A+1);n=strlen(A+1);for(i=n;i;i--) SAM::Ins(A[i]-'a')/*,cerr<<SAM::La<<'\n'*/,Tree::Ins(i,i,Ro[SAM::La]);SAM::BD();scanf("%d",&Q);while(Q--) Solve();cerr<<Tree::cnt<<'\n';
}