后缀自动机小专题
菜鸡万年更博。
全是菜题。
SP1811 LCS - Longest Common Substring
对一个串建立自动机,另一个串在自动机上遍历,失配时跳father更新即可。ans=遍历到的点的len的最大值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=25e4+3; char S[N],T[N]; struct SAM { int ch[26],fa,len; }sam[N<<1]; int tot=1,lst=1; void insert(int x) { int p=lst;lst=++tot;sam[lst].len=sam[p].len+1; for(;p&&!sam[p].ch[x];p=sam[p].fa) sam[p].ch[x]=lst; if(!p) sam[lst].fa=1; else { int q=sam[p].ch[x]; if(sam[p].len+1==sam[q].len) sam[lst].fa=q; else { int nq=++tot;sam[nq]=sam[q],sam[nq].len=sam[p].len+1; sam[lst].fa=sam[q].fa=nq; for(;sam[p].ch[x]==q;p=sam[p].fa) sam[p].ch[x]=nq; } } } int main() { scanf("%s%s",S+1,T+1); for(int i=1;S[i];++i) insert(S[i]-'a'); int now=1,len=0,Ans=0; for(int i=1;T[i];++i) { T[i]-='a'; if(sam[now].ch[T[i]]) ++len,now=sam[now].ch[T[i]]; else { while(now&&!sam[now].ch[T[i]]) now=sam[now].fa; if(!now) now=1,len=0; else len=sam[now].len+1,now=sam[now].ch[T[i]]; } Ans=max(Ans,len); } cout<<Ans; return 0; }
SP1812 LCS2 - Longest Common Substring II
可以跟上题一样做,也可以建个广义后缀自动机。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=2e5+5; int T,n; char s[N>>1]; int ch[N][26],len[N],fa[N]; int tot=1,lst=1; void insert(int x) { int p=lst;lst=++tot;len[lst]=len[p]+1; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst; if(!p) fa[lst]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[lst]=q; else { int nq=++tot;len[nq]=len[p]+1;fa[nq]=fa[q]; memmove(ch[nq],ch[q],sizeof(ch[q])); fa[q]=fa[lst]=nq; for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } int cnt[N],sa[N]; int mx[N],mn[N]; int main() { scanf("%s",s+1);int sl=strlen(s+1); lst=tot=1; for(int i=1;i<=sl;++i) insert(s[i]-'a'); for(int i=1;i<=tot;++i) ++cnt[len[i]]; for(int i=1;i<=sl;++i) cnt[i]+=cnt[i-1]; for(int i=1;i<=tot;++i) sa[cnt[len[i]]--]=i; memset(mn,0x3f,sizeof(mn)); while(~scanf("%s",s+1)) { int now=1,L=0,x; for(int i=1;s[i];++i) { x=s[i]-'a'; while(now&&!ch[now][x]) now=fa[now],L=len[now]; if(!now) now=1,L=0; else ++L,now=ch[now][x],mx[now]=max(mx[now],L); } for(int i=tot,u,f;i;--i) { u=sa[i],f=fa[u]; mx[f]=max(mx[f],min(mx[u],len[f])); mn[u]=min(mn[u],mx[u]);mx[u]=0; } } int Ans=0; for(int i=1;i<=tot;++i) Ans=max(Ans,mn[i]); cout<<Ans<<'\n'; return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound #include<bitset> using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=2e6+5; int id=-1; char s[100003]; struct SAM { int ch[26],fa,len; bitset<10> bst; }sam[N]; int lst,tot=1; int newnode(int p,int q,int x) { int nq=++tot;sam[nq]=sam[q];sam[nq].len=sam[p].len+1; sam[q].fa=nq; for(;p&&sam[p].ch[x]==q;p=sam[p].fa) sam[p].ch[x]=nq; return nq; } int insert(int p,int x) { if(sam[p].ch[x]) { int q=sam[p].ch[x]; if(sam[p].len+1==sam[q].len) { sam[q].bst[id]=1; return q; } else { int nq=newnode(p,q,x); sam[nq].bst[id]=1; return nq; } } else { int np=++tot;sam[np].len=sam[p].len+1;sam[np].bst[id]=1; for(;p&&!sam[p].ch[x];p=sam[p].fa) sam[p].ch[x]=np; if(!p) sam[np].fa=1; else { int q=sam[p].ch[x]; if(sam[p].len+1==sam[q].len) sam[np].fa=q; else sam[np].fa=newnode(p,q,x); } return np; } } void debug() { for(int i=1;i<=tot;++i) { for(int j=0;j<10;++j) cout<<sam[i].bst[j]<<' '; puts(""); } } int cnt[N],sa[N]; int main() { while(~scanf("%s",s+1)) { lst=1;++id; for(int i=1;s[i];++i) lst=insert(lst,s[i]-'a'); } for(int i=1;i<=tot;++i) ++cnt[sam[i].len]; for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1]; for(int i=tot;i;--i) sa[cnt[sam[i].len]--]=i; int Ans=0;++id; for(int i=tot;i>1;--i) { if(sam[sa[i]].bst.count()==(unsigned)id) Ans=max(Ans,sam[sa[i]].len); sam[sam[sa[i]].fa].bst|=sam[sa[i]].bst; } cout<<Ans; return 0; }
SP10570 LONGCS - Longest Common Substring
三倍经验
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=2e4+5; int T,n; char s[N>>1]; int ch[N][26],len[N],fa[N]; int tot=1,lst=1; void insert(int x) { int p=lst;lst=++tot;len[lst]=len[p]+1; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst; if(!p) fa[lst]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[lst]=q; else { int nq=++tot;len[nq]=len[p]+1;fa[nq]=fa[q]; memmove(ch[nq],ch[q],sizeof(ch[q])); fa[q]=fa[lst]=nq; for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } int cnt[N],sa[N]; int mx[N],mn[N]; int main() { T=read(); while(T--) { n=read();scanf("%s",s+1);int sl=strlen(s+1); lst=tot=1; memset(ch,0,sizeof(ch)); for(int i=1;i<=sl;++i) insert(s[i]-'a'); memset(cnt,0,sizeof(cnt)); for(int i=1;i<=tot;++i) ++cnt[len[i]]; for(int i=1;i<=sl;++i) cnt[i]+=cnt[i-1]; for(int i=1;i<=tot;++i) sa[cnt[len[i]]--]=i; memset(mn,0x3f,sizeof(mn)); for(int a=2;a<=n;++a) { scanf("%s",s+1); int now=1,L=0,x; for(int i=1;s[i];++i) { x=s[i]-'a'; while(now&&!ch[now][x]) now=fa[now],L=len[now]; if(!now) now=1,L=0; else ++L,now=ch[now][x],mx[now]=max(mx[now],L); } for(int i=tot,u,f;i;--i) { u=sa[i],f=fa[u]; mx[f]=max(mx[f],min(mx[u],len[f])); mn[u]=min(mn[u],mx[u]);mx[u]=0; } } int Ans=0; for(int i=1;i<=tot;++i) Ans=max(Ans,mn[i]); cout<<Ans<<'\n'; } return 0; }
P3181 [HAOI2016]找相同字符
求出每个子串在两个串中的出现次数,乘上每个点代表的不同子串个数len[p]-len[fa[p]]即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=8e5+5; char S[N>>2],T[N>>2]; int id; int ch[N][26],len[N],fa[N],siz[2][N]; int lst=1,tot=1; int newnode(int p,int x) { int q=ch[p][x],nq=++tot;len[nq]=len[p]+1; fa[nq]=fa[q],fa[q]=nq;memmove(ch[nq],ch[q],sizeof(ch[nq])); for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; return nq; } int insert(int p,int x) { if(ch[p][x]) { int q=ch[p][x]; if(len[p]+1==len[q]) { siz[id][q]=1; return q; } else { int nq=newnode(p,x); siz[id][nq]=1; return nq; } } else { int np=++tot;len[np]=len[p]+1,siz[id][np]=1; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np; if(!p) fa[np]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[np]=q; else fa[np]=newnode(p,x); } return np; } } int cnt[N],sa[N]; int main() { scanf("%s%s",S+1,T+1); lst=1,id=0; for(int i=1;S[i];++i) lst=insert(lst,S[i]-'a'); lst=id=1; for(int i=1;T[i];++i) lst=insert(lst,T[i]-'a'); for(int i=1;i<=tot;++i) ++cnt[len[i]]; for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1]; for(int i=tot;i;--i) sa[cnt[len[i]]--]=i; LL ans=0; for(int i=tot;i>1;--i) { siz[0][fa[sa[i]]]+=siz[0][sa[i]],siz[1][fa[sa[i]]]+=siz[1][sa[i]]; ans+=1ll*siz[0][sa[i]]*siz[1][sa[i]]*(len[sa[i]]-len[fa[sa[i]]]); } cout<<ans; return 0; }
P3975 [TJOI2015]弦论
求出每个点后有多少串,然后按照字典序像splay一样二分出来就就可以了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=1e6+5; int t,k; char s[N>>1]; int ch[N][26],len[N],fa[N],siz[N]; int tot=1,lst=1; void insert(int x) { int p=lst;lst=++tot;len[lst]=len[p]+1,siz[lst]=1; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst; if(!p) fa[lst]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[lst]=q; else { int nq=++tot;len[nq]=len[p]+1;fa[nq]=fa[q],siz[nq]=0; memmove(ch[nq],ch[q],sizeof(ch[q])); fa[q]=fa[lst]=nq; for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } int f[N]; int cnt[N],sa[N]; int main() { scanf("%s",s+1);int n=strlen(s+1); t=read(),k=read(); for(int i=1;i<=n;++i) insert(s[i]-'a'); for(int i=1;i<=tot;++i) ++cnt[len[i]]; for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1]; for(int i=1;i<=tot;++i) sa[cnt[len[i]]--]=i; for(int i=tot;i;--i) { if(!t) siz[sa[i]]=1; else siz[fa[sa[i]]]+=siz[sa[i]]; } siz[0]=siz[1]=0; for(int i=tot;i;--i) { f[sa[i]]=siz[sa[i]]; for(int j=0;j<26;++j) f[sa[i]]+=f[ch[sa[i]][j]]; } if(f[1]<k){puts("-1");return 0;} int now=1; while(k>siz[now]) { k-=siz[now]; for(int i=0;i<26;++i) { if(k>f[ch[now][i]]) k-=f[ch[now][i]]; else{putchar(i+'a');now=ch[now][i];break;} } } return 0; }
SP7258 SUBLEX - Lexicographical Substring Search
跟上个一样的题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=2e5+5; int T,k; char s[N>>1]; int ch[N][26],fa[N],len[N]; int lst=1,tot=1; void insert(int x) { int p=lst;lst=++tot;len[lst]=len[p]+1; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst; if(!p) fa[lst]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[lst]=q; else { int nq=++tot;fa[nq]=fa[q],len[nq]=len[p]+1; memmove(ch[nq],ch[q],sizeof(ch[q])); fa[lst]=fa[q]=nq; for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } int f[N],cnt[N],sa[N]; int main() { scanf("%s",s+1); for(int i=1;s[i];++i) insert(s[i]-'a'); for(int i=1;i<=tot;++i) ++cnt[len[i]]; for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1]; for(int i=tot;i;--i) sa[cnt[len[i]]--]=i; for(int i=tot;i;--i) { f[sa[i]]=1; for(int j=0;j<26;++j) f[sa[i]]+=f[ch[sa[i]][j]]; } T=read(); while(T--) { k=read(); int now=1; while(k) { for(int i=0;i<26;++i) { if(k>f[ch[now][i]]) k-=f[ch[now][i]]; else{putchar(i+'a');now=ch[now][i];--k;break;} } } puts(""); } return 0; }
P4070 [SDOI2016]生成魔咒
“每个点代表的不同子串个数是len[p]-len[fa[p]]”,每次插入ans加上len[lst]-len[fa[lst]]即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=2e5+5; int n;LL ans=0; int a[N]; map<int,int> ch[N];int len[N],fa[N]; int tot=1,lst=1; void insert(int x) { int p=lst;lst=++tot;len[lst]=len[p]+1; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst; if(!p) fa[lst]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[lst]=q; else { int nq=++tot;fa[nq]=fa[q],len[nq]=len[p]+1; fa[q]=fa[lst]=nq;ch[nq]=ch[q]; for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } ans+=len[lst]-len[fa[lst]]; } int main() { n=read(); for(int i=1;i<=n;++i) { insert(read()); cout<<ans<<'\n'; } return 0; }
BZOJ 3277 串
建广义后缀自动机,求出每个子串出现在几个串中看是否能产生贡献。每个点加上父亲的贡献,因为父亲一定会在这儿出现。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<string> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=2e5+5; int n,k; char s[N]; string S[N]; int ch[N][2],fa[N],len[N]; int tot=1,lst; void insert(int p,int x) { lst=++tot;len[lst]=len[p]+1; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst; if(!p) fa[lst]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[lst]=q; else { int nq=++tot;fa[nq]=fa[q],len[nq]=len[p]+1; fa[q]=fa[lst]=nq;memmove(ch[nq],ch[q],sizeof(ch[nq])); for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } int vis[N],tims[N]; void solve(string S,int x) { int now=1;int n=S.length(); for(int i=0;i<n;++i) { now=ch[now][S[i]-'a']; for(int p=now;p&&vis[p]!=x;p=fa[p]) vis[p]=x,++tims[p]; } } int sum[N]; void query(string S) { int n=S.length();int ans=0; int now=1; for(int i=0;i<n;++i) { now=ch[now][S[i]-'a']; ans+=sum[now]; } cout<<ans<<' '; } int cnt[N],sa[N]; int main() { n=read(),k=read(); for(int i=1;i<=n;++i) { scanf("%s",s+1); S[i]=string(s+1); int now=1;lst=1; for(int j=1;s[j];++j) insert(now,s[j]-='a'),now=ch[now][s[j]]; } for(int i=1;i<=n;++i) solve(S[i],i); for(int i=1;i<=tot;++i) sum[i]=(tims[i]>=k)*(len[i]-len[fa[i]]); for(int i=1;i<=tot;++i) ++cnt[len[i]]; for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1]; for(int i=1;i<=tot;++i) sa[cnt[len[i]]--]=i; for(int i=1;i<=tot;++i) sum[sa[i]]+=sum[fa[sa[i]]]; for(int i=1;i<=n;++i) query(S[i]); return 0; }
P4248 [AHOI2013]差异
把串倒着插入,两个子串的lcp长度就是他们在parent树上的祖先的长度。
记录每个点子树的后缀个数,计算与它父亲的其他儿子产生的贡献=siz[p]*siz[fa[p]]*len[fa[p]]。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=1e6+5; char s[N>>1]; int ch[N][26],len[N],fa[N],siz[N]; int lst=1,tot=1; void insert(int x) { int p=lst;lst=++tot;len[lst]=len[p]+1,siz[lst]=1; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst; if(!p) fa[lst]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[lst]=q; else { int nq=++tot;fa[nq]=fa[q],len[nq]=len[p]+1; fa[lst]=fa[q]=nq;memmove(ch[nq],ch[q],sizeof(ch[q])); for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } int sa[N],cnt[N]; int main() { scanf("%s",s+1);int n=strlen(s+1); for(int i=n;i;--i) insert(s[i]-'a'); for(int i=1;i<=tot;++i) ++cnt[len[i]]; for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1]; for(int i=tot;i;--i) sa[cnt[len[i]]--]=i; LL ans=0; for(int i=tot;i>1;--i) { ans+=1ll*siz[sa[i]]*siz[fa[sa[i]]]*len[fa[sa[i]]]; siz[fa[sa[i]]]+=siz[sa[i]]; } cout<<1ll*(n-1)*n*(n+1)/2-2*ans; return 0; }
P2178 [NOI2015]品酒大会
差不多和上个题一样。
两个r相似的串可以看做是从p,q开始的两个后缀的lcp长度为r。
最初ans1[r]求的是lcp恰好长为r的个数,ans2[r]求的是恰好为r的最大值。
最后再求个后缀和和后缀max就可以了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=6e5+5; const LL INF=0x3f3f3f3f3f3f3f3f; int n,a[N>>1]; char s[N>>1]; int ch[N][26],len[N],fa[N],siz[N]; LL mx[N],mn[N]; int lst=1,tot=1; void insert(int x,int v) { int p=lst;lst=++tot;len[lst]=len[p]+1,siz[lst]=1,mx[lst]=mn[lst]=v; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst; if(!p) fa[lst]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[lst]=q; else { int nq=++tot;len[nq]=len[p]+1,fa[nq]=fa[q]; fa[q]=fa[lst]=nq;memmove(ch[nq],ch[q],sizeof(ch[nq])); for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } LL ans1[N],ans2[N]; int sa[N],cnt[N]; int main() { memset(ans2,-0x3f,sizeof(ans2)); memset(mx,-0x3f,sizeof(mx));memset(mn,0x3f,sizeof(mn)); n=read();scanf("%s",s+1); for(int i=1;i<=n;++i) a[i]=read(); for(int i=n;i;--i) insert(s[i]-'a',a[i]); for(int i=1;i<=tot;++i) ++cnt[len[i]]; for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1]; for(int i=tot;i;--i) sa[cnt[len[i]]--]=i; for(int i=tot;i;--i) { ans1[len[fa[sa[i]]]]+=1ll*siz[fa[sa[i]]]*siz[sa[i]]; if(mx[sa[i]]!=-INF&&mn[sa[i]]!=INF&&mx[fa[sa[i]]]!=-INF&&mn[fa[sa[i]]]!=INF) { ans2[len[fa[sa[i]]]]=max(ans2[len[fa[sa[i]]]],1ll*mx[sa[i]]*mx[fa[sa[i]]]); ans2[len[fa[sa[i]]]]=max(ans2[len[fa[sa[i]]]],1ll*mn[sa[i]]*mn[fa[sa[i]]]); } siz[fa[sa[i]]]+=siz[sa[i]]; mx[fa[sa[i]]]=max(mx[fa[sa[i]]],mx[sa[i]]); mn[fa[sa[i]]]=min(mn[fa[sa[i]]],mn[sa[i]]); } for(int i=n-2;~i;--i) ans1[i]+=ans1[i+1],ans2[i]=max(ans2[i],ans2[i+1]); for(int i=0;i<n;++i) cout<<ans1[i]<<' '<<(ans1[i]?ans2[i]:0)<<'\n'; return 0; }
CF873F Forbidden Indices
被禁止就当做没出现,否则当做出现。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=4e5+5; int n; char s[N>>1],t[N>>1]; int ch[N][26],len[N],fa[N]; int siz[N]; int tot=1,lst=1; void insert(int x,bool flag) { int p=lst;lst=++tot;len[lst]=len[p]+1,siz[lst]=flag; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst; if(!p) fa[lst]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[lst]=q; else { int nq=++tot;len[nq]=len[p]+1,fa[nq]=fa[q]; fa[lst]=fa[q]=nq;memmove(ch[nq],ch[q],sizeof(ch[q])); for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } int cnt[N],sa[N]; int main() { n=read();scanf("%s%s",s+1,t+1); for(int i=1;i<=n;++i) insert(s[i]-'a',(t[i]!='1')); for(int i=1;i<=tot;++i) ++cnt[len[i]]; for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1]; for(int i=tot;i;--i) sa[cnt[len[i]]--]=i; for(int i=tot;i;--i) siz[fa[sa[i]]]+=siz[sa[i]]; LL ans=0; for(int i=1;i<=tot;++i) ans=max(ans,1ll*len[i]*siz[i]); cout<<ans; return 0; }
CodeChef Killjee and k-th letter
做不下去了, 还没有看懂它建后缀树的地方,等待zbq解答ing..
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<vector> #include<stack> #include<queue> #include<set> #include<map> #define pb push_back #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define lb lower_bound #define ub upper_bound using namespace std; typedef long long LL; typedef unsigned long long ULL; inline LL read() { char c=getchar();LL num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } const int N=4e5+5; int n;LL sum[N]; char s[N>>1]; int son[N][26]; int ch[N][26],len[N],fa[N],rig[N],pos[N]; int lst=1,tot=1; void insert(int x,int i) { int p=lst;lst=++tot;len[lst]=len[p]+1;++rig[lst],pos[lst]=i; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=lst; if(!p) fa[lst]=1; else { int q=ch[p][x]; if(len[p]+1==len[q]) fa[lst]=q; else { int nq=++tot;fa[nq]=fa[q],len[nq]=len[p]+1; fa[q]=fa[lst]=nq;memmove(ch[nq],ch[q],sizeof(ch[nq])); for(;ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } void prework() { static int cnt[N],sa[N]; for(int i=1;i<=tot;++i) ++cnt[len[i]]; for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1]; for(int i=1;i<=tot;++i) sa[cnt[len[i]]--]=i; for(int i=tot,x,y;i;--i) { x=sa[i],y=fa[sa[i]]; rig[y]+=rig[x],pos[y]=pos[x]; son[y][s[pos[x]-len[y]]-'a']=x; } } int dfn[N],bound; void dfs(int u) { dfn[++bound]=u; for(int i=0;i<26;++i) if(son[u][i]) dfs(son[u][i]); } LL calc(int l,int r) { return 1ll*(l+r)*(r-l+1)/2; } int query(LL k) { int l=1,r=tot,mid; while(l<=r) { mid=(l+r)>>1; if(sum[mid]<k) l=mid+1; else r=mid-1; } k-=sum[l-1];int t=dfn[l],h=len[fa[t]]+1;l=h,r=len[t]; while(l<=r) { int mid=(l+r)>>1; if(1ll*rig[t]*calc(h,mid)<k) l=mid+1; else r=mid-1; } k-=1ll*rig[t]*calc(h,l-1); k=(k-1)%l+1; return s[pos[t]-k+1]; } int main() { scanf("%s",s+1);n=strlen(s+1); reverse(s+1,s+n+1); for(int i=1;i<=n;++i) insert(s[i]-'a',i); prework();dfs(1); for(int i=1;i<=bound;++i) sum[i]=sum[i-1]+1ll*calc(len[fa[dfn[i]]]+1,len[dfn[i]])*rig[dfn[i]]; int Q=read(),G=0; while(Q--) { int p,ans;LL m;p=read(),m=read(); LL k=1ll*p*G%m+1; G+=(ans=query(k)); cout<<(char)ans<<'\n'; } return 0; }