luogu P4770 [NOI2018]你的名字
阿新 • • 發佈:2018-12-15
這題寫死我了,因為一點點鬼會注意到的細節調好久,,,
先考慮前17個點,如果我們不考慮T的不同子串限制,那麼就是把T放在S的SAM上匹配,答案為\(\sum_{i=1}^{|T|}i-l[i]\)(匹配長度).考慮怎麼判重,對T也構建一個SAM,由於每個字首的貢獻答案的子串左端點在\([1,i-l[i])\)之間,對應到parent樹上就是若干條鏈,那麼每個位置的貢獻就是對應鏈的沒有被前面的鏈覆蓋的部分
後面的點,首先我們每次要跳到S[1...l-1]所對應的點,然後把T放在上面匹配,不過可能匹配過程中,S匹配上的子串沒有在[l,r]之間的,這時候我們要跳parent,去掉匹配子串的前面一些部分,使得[l,r]之間出現對應的串.這個可以通過SAM每個節點開一棵線段樹維護endpos集合,通過線段樹合併維護,每次查詢對應狀態[l,r]中最大的結束位置-匹配長度+1是否\(\ge l\)
(這種做法有點問題,可能跳parent會因為串的前面部分而跳過頭,導致匹配長度變小,從而導致答案變大,所以跳完後要嘗試往回跳orz)
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define LL long long #define il inline #define re register #define db double using namespace std; const int N=1000000+10; il LL rd() { LL x=0,w=1;char ch=0; while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();} while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} return x*w; } char cc[N],ss[N]; int n,m; int an[N>>1],bk[N>>1],aa[N],loc[N>>1],st[N],tp; int s[N*22],ch[N*22][2],rt[N],tot; il void inst(int o1,int o2,int x) { int l=1,r=n; s[o1]=max(s[o2],x); while(l<r) { int mid=(l+r)>>1; if(x<=mid) { ch[o1][0]=++tot,ch[o1][1]=ch[o2][1]; o1=ch[o1][0],o2=ch[o2][0]; r=mid; } else { ch[o1][0]=ch[o2][0],ch[o1][1]=++tot; o1=ch[o1][1],o2=ch[o2][1]; l=mid+1; } s[o1]=max(s[o2],x); } } int merge(int o1,int o2) { if(!o1||!o2) return o1+o2; //if(o1==o2) return o1; int o=++tot; s[o]=max(s[o1],s[o2]); ch[o][0]=merge(ch[o1][0],ch[o2][0]),ch[o][1]=merge(ch[o1][1],ch[o2][1]); return o; } int quer(int o,int l,int r,int ll,int rr) { if(!o) return s[0]; if(ll<=l&&r<=rr) return s[o]; int mid=(l+r)>>1,an=s[0]; if(ll<=mid) an=/*max(an,*/quer(ch[o][0],l,mid,ll,rr)/*)*/; if(rr>mid) an=max(an,quer(ch[o][1],mid+1,r,ll,rr)); return an; } struct SAM { int fa[N],len[N],a[N],la,tt; int ch[N][26]; bool v[N]; SAM(){la=tt=1;} il void inst(char c,int id) { int np=++tt,p=la; la=np,a[np]=id,len[np]=len[p]+1; while(p&&!ch[p][c-'a']) ch[p][c-'a']=np,p=fa[p]; if(!p) fa[np]=1; else { int q=ch[p][c-'a']; if(len[q]==len[p]+1) fa[np]=q; else { int nq=++tt; fa[nq]=fa[q],len[nq]=len[p]+1,fa[q]=fa[np]=nq; for(int i=0;i<26;++i) ch[nq][i]=ch[q][i]; while(p&&ch[p][c-'a']==q) ch[p][c-'a']=nq,p=fa[p]; } } } il void clear() { memset(ch,0,4*26*(tt+2)); memset(v,0,1*(tt+2)); memset(fa,0,4*(tt+2)); memset(len,0,4*(tt+2)); la=tt=1; } }a,b; int main() { scanf("%s",ss+1); n=strlen(ss+1); for(int i=1;i<=n;++i) a.inst(ss[i],i); for(int i=1;i<=a.tt;++i) ++bk[a.len[i]]; for(int i=1;i<=n;++i) bk[i]+=bk[i-1]; for(int i=1;i<=a.tt;++i) aa[bk[a.len[i]]--]=i; memset(s,-63,sizeof(s)); for(int i=a.tt,las;i;--i) { if(a.a[aa[i]]) las=rt[aa[i]],inst(rt[aa[i]]=++tot,las,a.a[aa[i]]); rt[a.fa[aa[i]]]=merge(rt[a.fa[aa[i]]],rt[aa[i]]); } loc[0]=1; for(int i=1,nw=1;i<=n;++i) loc[i]=nw=a.ch[nw][ss[i]-'a']; int q=rd(); while(q--) { scanf("%s",cc+1); m=strlen(cc+1); LL ans=0; int l=rd(),r=rd(); for(int i=1,nw=loc[l-1],kk=0;i<=m;++i) { int w=cc[i]-'a'; while(nw&&!a.ch[nw][w]) nw=a.fa[nw],kk=a.len[nw]; if(!nw) nw=1,kk=0; else nw=a.ch[nw][w],++kk; tp=0; while(nw&&quer(rt[nw],1,n,l,r)-kk+1<l) st[++tp]=nw,nw=a.fa[nw],kk=a.len[nw]; if(tp) { while(quer(rt[nw],1,n,l,r)-kk+1>=l) ++kk,nw=(kk<=a.len[nw])?nw:st[tp--]; --kk,nw=(kk>a.len[a.fa[nw]])?nw:a.fa[nw]; } an[i]=kk; } b.clear(); for(int i=1;i<=m;++i) b.inst(cc[i],0); b.v[0]=1; for(int i=1,nw=1;i<=m;++i) { int w=cc[i]-'a',p=nw=b.ch[nw][w]; while(!b.v[p]&&b.len[p]>=an[i]) ans+=b.len[p]-max(b.len[b.fa[p]],an[i]),b.v[p]=1,p=b.fa[p]; } printf("%lld\n",ans); } return 0; }