1. 程式人生 > >LOJ #6031 字符串

LOJ #6031 字符串

etc mage pre c++ ans else ret mem +=

Description

技術分享圖片

Solution

\(k\) 值較小時,發現詢問串比較多,串長比較小
然後對 \(Q\) 個詢問區間離線跑莫隊,一次考慮每一個區間的貢獻
假設一個區間 \([i,j]\) 出現的次數是 \(c[i][j]\),然後 \(O(k^2)\) 求出每一個區間的貢獻,乘上 \(c[i][j]\) 就是答案

\(k\) 值較大時,詢問次數比較少,串長比較大
考慮與詢問次數有關的做法
對於每一個詢問,預處理出 \(w\) 的每一個前綴在 \(S\)\(SAM\) 中匹配到的位置和匹配的長度
右端點固定時,左端點移動形成的串就是這個右端點對應的前綴的後綴,每一次跳父親就可以跳到

倍增到合法長度的節點即可

顯然 \(k\)\(\sqrt{10^5}\) 時最優

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>void gi(T &x){
    int f;char c;
    for (f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
    for (x=0;c<='9'
&&c>='0';c=getchar())x=x*10+(c&15);x*=f; } const int N=2e5+10,M=320,B=20; int n,m,Q,K,ch[N][26],fa[N],cur=1,cnt=1,len[N],sz[N],sa[N],c[N],g[N]; char s[N]; inline void ins(int c){ int p=cur;cur=++cnt;len[cur]=len[p]+1; for(;p && !ch[p][c];p=fa[p])ch[p][c]=cur; if
(!p)fa[cur]=1; else{ int q=ch[p][c]; if(len[p]+1==len[q])fa[cur]=q; else{ int nt=++cnt;len[nt]=len[p]+1; memcpy(ch[nt],ch[q],sizeof(ch[q])); fa[nt]=fa[q];fa[q]=fa[cur]=nt; for(;p && ch[p][c]==q;p=fa[p])ch[p][c]=nt; } }sz[cur]=1; } inline void priwork(){ for(int i=1;i<=cnt;i++)c[len[i]]++; for(int i=1;i<=n;i++)c[i]+=c[i-1]; for(int i=cnt;i>=1;i--)sa[c[len[i]]--]=i; for(int i=cnt;i>=1;i--)sz[fa[sa[i]]]+=sz[sa[i]]; } struct D{int l,r;}e[N]; struct data{int l,r,id;}q[N]; inline bool comp(data i,data j){return i.l/B!=j.l/B?i.l/B<j.l/B:i.r<j.r;} namespace solo{ char w[N][M];int v[M][M];ll ans[N]; inline void add(int x){v[e[x].l][e[x].r]++;} inline void del(int x){v[e[x].l][e[x].r]--;} inline ll solve(int x){ int len=strlen(w[x]+1),p,c;ll ret=0; for(int i=1;i<=len;i++){ p=1; for(int j=i;j<=len;j++){ c=w[x][j]-'a'; if(!ch[p][c])break; p=ch[p][c]; ret+=v[i][j]*sz[p]; } } return ret; } void main(){ for(int i=1;i<=Q;i++){ scanf("%s",w[i]+1); gi(q[i].l);gi(q[i].r);q[i].id=i;q[i].l++;q[i].r++; } sort(q+1,q+Q+1,comp); int l=1,r=0; for(int i=1;i<=Q;i++){ while(r<q[i].r)add(++r); while(l>q[i].l)add(--l); while(r>q[i].r)del(r--); while(l<q[i].l)del(l++); ans[q[i].id]=solve(q[i].id); } for(int i=1;i<=Q;i++)printf("%lld\n",ans[i]); } } namespace sol{ char w[N];int pos[N],f[N][20]; inline int qry(int x,int y){ if(len[x]<y)return 0; for(int i=19;i>=0;i--) if(f[x][i] && len[f[x][i]]>=y)x=f[x][i]; return sz[x]; } void main(){ int x,y,le,p,now; for(int i=1;i<=cnt;i++)f[i][0]=fa[i]; for(int j=1;j<20;j++) for(int i=1;i<=cnt;i++)f[i][j]=f[f[i][j-1]][j-1]; for(int i=1;i<=Q;i++){ scanf("%s",w+1);le=strlen(w+1); p=1;now=0; for(int j=1;j<=le;j++){ int c=w[j]-'a'; if(ch[p][c])p=ch[p][c],now++; else{ while(p>1 && !ch[p][c])p=fa[p]; if(ch[p][c])now=len[p]+1,p=ch[p][c]; else now=0; } pos[j]=p;g[j]=now; } gi(x);gi(y);x++;y++; ll ret=0; for(int j=x;j<=y;j++) if(g[e[j].r]>=e[j].r-e[j].l+1) ret+=qry(pos[e[j].r],e[j].r-e[j].l+1); printf("%lld\n",ret); } } } int main(){ freopen("pp.in","r",stdin); freopen("pp.out","w",stdout); cin>>n>>m>>Q>>K; scanf("%s",s+1); for(int i=1;i<=n;i++)ins(s[i]-'a'); priwork(); for(int i=1;i<=m;i++)gi(e[i].l),gi(e[i].r),e[i].l++,e[i].r++; if(K<M)solo::main(); else sol::main(); return 0; }

LOJ #6031 字符串