「后缀数组」学习笔记
理论知识
详见 OI Wiki 。
模板
后缀排序
一切有关后缀数组问题的必备板子。
求后缀数组模板题,OI Wiki 有详解 。
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=1e6+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N]; char s[N]; void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init() { int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif cin>>s+1; n=strlen(s+1); init(); for(int i=1;i<=n;i++) cout<<sa[i]<<' '; }
可重叠最长重复子串
求
即求
注意
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=2e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N],height[N],ans; char s[N]; void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init() { int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n); cin>>s+1; init(); for(int i=1;i<=n;i++) ans=max(ans,height[i]); cout<<ans; }
应用
数组基本应用
不同子串个数
所有子串的个数为
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=2e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,q,sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N],height[N],ans; char s[N]; int sta[N],top,a[N],b[N]; struct aa { int l,r,id; }e[N]; bool cmp(aa a,aa b) {return a.l==b.l?a.r>b.r:a.l<b.l;} void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init() { int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n); cin>>s+1; init(); for(int i=1;i<=n;i++) ans+=height[i]; cout<<n*(n+1)/2-ans; }
Milk Patterns G
求出现最少
出现至少
即求出没连续
可以用单调队列
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=2e6+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');} void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);} int n,K,sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N],height[N],ans; int s[N]; deque<int>q; void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init() { int m=1000000,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n),read(K); K--; for(int i=1;i<=n;i++) read(s[i]); init(); for(int i=1;i<=n;i++) { while(!q.empty()&&height[q.front()]>=height[i]) q.pop_front(); q.push_front(i); while(!q.empty()&&q.back()<=i-K) q.pop_back(); if(i>=K) ans=max(ans,height[q.back()]); } write(ans); }
结合 表
我们知道
很多时候
Yet Another LCP Problem
对于每一组给定的
我们定义
至于为什么
我们知道
结合单调栈求
先将
定义
接下来单调栈维护的过程与
那么
至于
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=4e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,m,ans,sum,sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N],height[N],l[N],r[N],a[N],b[N],c[N],mi[N][30],val[N]; char s[N],t[N]; int sta[N],top; void clean() { memset(rk,0,sizeof(rk)); memset(sa,0,sizeof(sa)); memset(id,0,sizeof(id)); memset(oldrk,0,sizeof(rk)); memset(cnt,0,sizeof(cnt)); memset(key,0,sizeof(key)); memset(height,0,sizeof(height)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); } void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init(char s[],int n) { int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } void RMQ() { memset(mi,0x3f,sizeof(mi)); for(int i=1;i<=n;i++) mi[i][0]=height[i]; for(int j=1;j<=log2(n);j++) for(int i=1;i<=n-(1<<j)+1;i++) mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]); } int ask(int l,int r) { int t=log2(r-l+1); return min(mi[l][t],mi[r-(1<<t)+1][t]); } bool cmp(int a,int b) {return rk[a]<rk[b];} int calc(int x[],int len) { sort(x+1,x+1+len,cmp); for(int i=2;i<=len;i++) if(x[i]==x[i-1]) val[i]=n-x[i]+1; else val[i]=ask(rk[x[i-1]]+1,rk[x[i]]); sum=ans=top=0; for(int i=1;i<=len;i++) { while(val[sta[top]]>val[i]) top--; l[i]=i-sta[top]; sta[++top]=i; } val[len+1]=-1,sta[++top]=len+1; for(int i=len;i>=1;i--) { while(val[sta[top]]>=val[i]) top--; r[i]=sta[top]-i; sta[++top]=i; } for(int i=1;i<=len;i++) ans+=l[i]*r[i]*val[i]; return ans; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n),read(m); cin>>s+1; init(s,n); RMQ(); for(int i=1,len1,len2;i<=m;i++) { read(len1),read(len2); for(int j=1;j<=len1;j++) read(a[j]), c[j]=a[j]; for(int j=1;j<=len2;j++) read(b[j]), c[len1+j]=b[j]; cout<<calc(c,len1+len2)-calc(a,len1)-calc(b,len2)<<endl; } }
相似子串
我们发现所有子串的左端点都在后缀数组中出现过,问题是考虑右端点。
发现例如后缀 abcd
,可以将其分为 a
、ab
、abc
、abcd
四个子串,若其与上一个后缀的 ab
,则新的子串就剩下 abc
、abcd
。
由此可得每个后缀对子串个数的贡献为
不妨使用前缀和维护,利用二分查找出第
对于每次查询
问题来到如何处理公共后缀,不难想到建一个反串类似的维护,而问题是子串
定义
虽然这样求 就是个很好的例子,
aba``` 的
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=1e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,m,ans,sa[N][2],rk[N],id[N],oldrk[N],cnt[N],key[N],height[N][2],sum[N][2],ls[N],mi[2][N][30]; char s[N],t[N]; void clean() { memset(rk,0,sizeof(rk)); // memset(sa,0,sizeof(sa)); memset(id,0,sizeof(id)); memset(oldrk,0,sizeof(rk)); memset(cnt,0,sizeof(cnt)); memset(key,0,sizeof(key)); // memset(height,0,sizeof(height)); memset(ls,0,sizeof(ls)); } void count_sort(int n,int m,bool d) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]][d]=id[i], cnt[key[i]]--; } void init(char s[],int n,bool d) { clean(); int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m,d); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i][d]>w) num++, id[num]=sa[i][d]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m,d); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i][d]]!=oldrk[sa[i-1][d]]||oldrk[sa[i][d]+w]!=oldrk[sa[i-1][d]+w]), rk[sa[i][d]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1][d]+k]) k++; height[rk[i]][d]=k; } for(int i=1;i<=n;i++) ls[i]=n-sa[i][d]+1-height[i][d], sum[i][d]=sum[i-1][d]+ls[i]; } void RMQ(bool d) { memset(mi[d],0x3f,sizeof(mi[d])); for(int i=1;i<=n;i++) mi[d][i][0]=height[i][d]; for(int j=1;j<=log2(n);j++) for(int i=1;i<=n-(1<<j)+1;i++) mi[d][i][j]=min(mi[d][i][j-1],mi[d][i+(1<<(j-1))][j-1]); } int LCP(int l,int r,bool d) { l++; int t=log2(r-l+1); return min(mi[d][l][t],mi[d][r-(1<<t)+1][t]); } int ask(int x,int y,bool d) { int l=1,r=n,mid,pos_x,pos_y; while(l<=r) { mid=(l+r)>>1; if(sum[mid][0]<x) l=mid+1; if(sum[mid][0]>x) r=mid-1,pos_x=mid; if(sum[mid][0]==x) { pos_x=mid; break; } } l=1,r=n; while(l<=r) { mid=(l+r)>>1; if(sum[mid][0]<y) l=mid+1; if(sum[mid][0]>y) r=mid-1,pos_y=mid; if(sum[mid][0]==y) { pos_y=mid; break; } } int len_x=height[pos_x][0]+x-sum[pos_x-1][0],len_y=height[pos_y][0]+y-sum[pos_y-1][0]; if(d==1) pos_x=rk[n-(sa[pos_x][0]+len_x-1)+1], pos_y=rk[n-(sa[pos_y][0]+len_y-1)+1]; if(pos_x>pos_y) swap(pos_x,pos_y); return pos_x==pos_y?min(len_x,len_y):min(min(len_x,len_y),LCP(pos_x,pos_y,d)); } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n),read(m); cin>>s+1; for(int i=1;i<=n;i++) t[n-i+1]=s[i]; init(s,n,0),init(t,n,1); RMQ(0),RMQ(1); for(int i=1,x,y;i<=m;i++) { read(x),read(y); if(x>sum[n][0]||y>sum[n][0]) puts("-1"); else cout<<ask(x,y,0)*ask(x,y,0)+ask(x,y,1)*ask(x,y,1)<<endl; } }
优秀的拆分
枚举连续串的长度
正反存两个串,处理出
定义
那么答案就等于
枚举
因为是区间修改,考虑使用差分维护。
使用结构体封装
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=3e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');} void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);} int T,n,pre[N],suf[N],ans; struct aa { char s[N]; int rk[N],sa[N],id[N],key[N],oldrk[N],cnt[N],height[N],mi[N][30]; void clean() { memset(rk,0,sizeof(rk)); memset(sa,0,sizeof(sa)); memset(id,0,sizeof(id)); memset(oldrk,0,sizeof(rk)); memset(cnt,0,sizeof(cnt)); memset(key,0,sizeof(key)); memset(height,0,sizeof(height)); } void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init(char s[],int n) { int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } void ST(int n) { memset(mi,0x3f,sizeof(mi)); for(int i=1;i<=n;i++) mi[i][0]=height[i]; for(int j=1;j<=log2(n);j++) for(int i=1;i<=n-(1<<j)+1;i++) mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]); } int lcp(int l,int r) { l=rk[l],r=rk[r]; if(l>r) swap(l,r); l++; int t=log2(r-l+1); return min(mi[l][t],mi[r-(1<<t)+1][t]); } }a,b; signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(T); while(T--) { cin>>a.s+1; n=strlen(a.s+1); for(int i=1;i<=n;i++) b.s[i]=a.s[n-i+1]; a.clean(),b.clean(); a.init(a.s,n),b.init(b.s,n); a.ST(n),b.ST(n); for(int i=1;i<=n;i++) pre[i]=suf[i]=0; for(int len=1;len<=n/2;len++) for(int i=1;i<=n;i+=len) { int l1=i,r1=i+len,l2=n-(r1-1)+1,r2=n-(l1-1)+1; int lcp=min(a.lcp(l1,r1),len),lcs=min(b.lcp(l2,r2),len-1); if(lcp+lcs>=len) pre[l1-lcs]++, pre[l1+lcp-len+1]--, suf[r1-lcs+len-1]++, suf[r1+lcp]--; } for(int i=1;i<=n;i++) pre[i]+=pre[i-1],suf[i]+=suf[i-1]; ans=0; for(int i=1;i<n;i++) ans+=pre[i+1]*suf[i]; write(ans),puts(""); } }
结合单调栈
因为
通常思路有两种,本人主要采取类似于 广告印刷 这道题,处理出每个
差异
结合单调栈板子题。
对于
不难计算出
问题主要是如何求
根据
通过单调栈,处理出
那么最后答案就是
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=5e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N],height[N],ans,l[N],r[N]; char s[N]; int sta[N],top; void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init() { int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif cin>>s+1; n=strlen(s+1); init(); for(int i=1;i<=n;i++) { while(height[sta[top]]>height[i]) top--; l[i]=i-sta[top]; sta[++top]=i; } sta[++top]=n+1,height[n+1]=-1; for(int i=n;i>=1;i--) { while(height[sta[top]]>=height[i]) top--; r[i]=sta[top]-i; ans-=2ll*l[i]*r[i]*height[i]; sta[++top]=i; } ans+=1ll*n*(n-1)*(n+1)/2; cout<<ans; }
找相同字符
设两个串为 #
隔开,定义
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=4e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,m2,m1,ans,sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N],height[N],l[N],r[N]; char s[N],t2[N],t1[N]; int sta[N],top; void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init(char s[],int n) { memset(rk,0,sizeof(rk)); memset(sa,0,sizeof(sa)); memset(id,0,sizeof(id)); memset(oldrk,0,sizeof(rk)); memset(cnt,0,sizeof(cnt)); memset(key,0,sizeof(key)); memset(height,0,sizeof(height)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } int calc(char s[],int len) { init(s,len); ans=top=0; for(int i=1;i<=len;i++) { while(height[sta[top]]>height[i]) top--; l[i]=i-sta[top]; sta[++top]=i; } int x=height[len+1]; sta[++top]=len+1,height[len+1]=-1; for(int i=len;i>=1;i--) { while(height[sta[top]]>=height[i]) top--; r[i]=sta[top]-i; ans+=l[i]*r[i]*height[i]; sta[++top]=i; } height[len+1]=x; return ans; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif cin>>t1+1>>t2+1; m1=strlen(t1+1),m2=strlen(t2+1); for(int i=1;i<=m1;i++) s[i]=t1[i]; s[m1+1]='#'; for(int i=1;i<=m2;i++) s[m1+1+i]=t2[i]; n=m1+m2+1; cout<<calc(s,n)-calc(t2,m2)-calc(t1,m1); }
Yet Another LCP Problem
上面已有。
多个串拼合
大多数找不同字符串的公共串的问题都需要将多个字符串拼起来处理。
字符加密
将
若
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=2e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N]; char s[N]; void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init() { int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif cin>>s+1; n=strlen(s+1); for(int i=1;i<=n;i++) s[i+n]=s[i]; n*=2; init(); for(int i=1;i<=n;i++) if(sa[i]<=n/2) cout<<s[sa[i]+n/2-1]; }
公共串
将
那么对于每个区间
使用双指针加单调队列维护即可。
接下来详细解释怎么统计是否每个
定义两个函数
先处理出每个
定义
那么当
同时因为
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=1e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,m,ans,sum,sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N],height[N],l[N],r[N],c[N],v[N],L[N],R[N]; char s[N],t[N]; deque<int>q; void clean() { memset(rk,0,sizeof(rk)); memset(sa,0,sizeof(sa)); memset(id,0,sizeof(id)); memset(oldrk,0,sizeof(rk)); memset(cnt,0,sizeof(cnt)); memset(key,0,sizeof(key)); memset(height,0,sizeof(height)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); } void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init(char s[],int n) { int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } void add(int x,int &sum) { if(c[x]==0) return ; v[c[x]]++; sum+=(v[c[x]]==1); } void del(int x,int &sum) { if(c[x]==0) return ; v[c[x]]--; sum-=(v[c[x]]==0); } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(m); for(int i=1;i<=m;i++) { cin>>t+1; int len=strlen(t+1); L[i]=n+1; for(int j=1;j<=len;j++) s[++n]=t[j]; R[i]=n; s[++n]=i+'#'; } init(s,n); for(int i=1;i<=m;i++) for(int j=L[i];j<=R[i];j++) c[rk[j]]=i; add(1,sum); for(int l=1,r=2;r<=n;r++) { while(!q.empty()&&height[q.front()]>=height[r]) q.pop_front(); q.push_front(r); add(r,sum); if(sum>=m) { while(sum>=m&&l<=r-1) del(l,sum), l++; l--; add(l,sum); } while(!q.empty()&&q.back()<=l) q.pop_back(); if(sum==m) ans=max(ans,height[q.back()]); } cout<<ans; }
Sandy 的卡片
查分后变为 公共串 ,因为差分后长度会
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=2e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,m,ans,sum,sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N],height[N],l[N],r[N],c[N],v[N],L[N],R[N]; int s[N],t[N]; deque<int>q; void clean() { memset(rk,0,sizeof(rk)); memset(sa,0,sizeof(sa)); memset(id,0,sizeof(id)); memset(oldrk,0,sizeof(rk)); memset(cnt,0,sizeof(cnt)); memset(key,0,sizeof(key)); memset(height,0,sizeof(height)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); } void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init(int s[],int n) { int m=5000,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } void add(int x,int &sum) { if(c[x]==0) return ; v[c[x]]++; sum+=(v[c[x]]==1); } void del(int x,int &sum) { if(c[x]==0) return ; v[c[x]]--; sum-=(v[c[x]]==0); } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(m); for(int i=1,len;i<=m;i++) { cin>>len; L[i]=n+1; for(int j=1;j<=len;j++) { read(t[j]); if(j>=2) s[++n]=2000+t[j]-t[j-1]; } R[i]=n; s[++n]=i+2000; } init(s,n); for(int i=1;i<=m;i++) for(int j=L[i];j<=R[i];j++) c[rk[j]]=i; add(1,sum); for(int l=1,r=2;r<=n;r++) { while(!q.empty()&&height[q.front()]>=height[r]) q.pop_front(); q.push_front(r); add(r,sum); if(sum>=m) { while(sum>=m&&l<=r-1) del(l,sum), l++; l--; add(l,sum); } while(!q.empty()&&q.back()<=l) q.pop_back(); if(sum==m) ans=max(ans,height[q.back()]); } cout<<ans+1; }
找相同字符
上面已有,由于涉及将两个串拼起来的思路,所以也放在了这里。
Yet Another LCP Problem
上面已有,同样的涉及将两个串拼起来。
结合莫队
喵星球上的点名
将所有姓名拼起来,中间用不同分隔符断开,处理出
对于每一个查询串,发现其对应原串可以匹配位置可以用区间
对于每个后缀处理出其对应那个人,用
于是问题一转变成区间数颜色问题,最基本的莫队维护即可,可见 HH 的项链 。
至于第二问,不妨在添加和删除操作时传入参数
当新添加入该串时,另其
点击查看代码
#include<bits/stdc++.h> // #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=5e6+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');} void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);} int n,m,t,tot,tot_m,s[N],id[N],rk[N],sa[N],cnt[N],oldrk[N],key[N],height[N],a[N],pos[N],anss[N],ans,ans_tot[N]; struct aa { int l,r,id; }e[N]; bool cmp(aa a,aa b) {return pos[a.l]==pos[b.l]?a.r<b.r:a.l<b.l;} void clean() { memset(rk,0,sizeof(rk)); memset(sa,0,sizeof(sa)); memset(id,0,sizeof(id)); memset(oldrk,0,sizeof(rk)); memset(cnt,0,sizeof(cnt)); memset(key,0,sizeof(key)); memset(height,0,sizeof(height)); } void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init(int s[],int n) { int m=300000,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } void add(int &ans,int x,int i) { cnt[a[x]]++; if(cnt[a[x]]==1) ans++, ans_tot[a[x]]+=tot_m-i+1; } void del(int &ans,int x,int i) { cnt[a[x]]--; if(cnt[a[x]]==0) ans--, ans_tot[a[x]]-=tot_m-i+1; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n),read(m); t=sqrt(n); int tmp=10000; for(int i=1,len;i<=n;i++) { read(len); for(int j=1;j<=len;j++) read(s[++tot]), a[tot]=i; s[++tot]=++tmp; read(len); for(int j=1;j<=len;j++) read(s[++tot]), a[tot]=i; s[++tot]=++tmp; pos[i]=(i-1)/t+1; } init(s,tot); for(int i=1,len;i<=m;i++) { read(len); int ll=1,rr=tot; for(int j=1,x;j<=len;j++) { read(x); int l=ll,r=rr,mid; while(l<=r) { mid=(l+r)>>1; if(s[sa[mid]+j-1]<x) l=mid+1; else r=mid-1; } int ls=l; l=ll,r=rr; while(l<=r) { int mid=(l+r)>>1; if(s[sa[mid]+j-1]<=x) l=mid+1; else r=mid-1; } ll=ls,rr=r; } if(ll<=rr) e[++tot_m]={ll,rr,i}; } sort(e+1,e+1+tot_m,cmp); int l=1,r=0; memset(cnt,0,sizeof(cnt)); for(int i=1;i<=tot_m;i++) { while(l>e[i].l) add(ans,sa[--l],i); while(l<e[i].l) del(ans,sa[l++],i); while(r<e[i].r) add(ans,sa[++r],i); while(r>e[i].r) del(ans,sa[r--],i); anss[e[i].id]=ans; } for(int i=1;i<=m;i++) write(anss[i]),puts(""); for(int i=1;i<=n;i++) write(ans_tot[i]),cout<<' '; }
结合单调队列
因为
下面的例题前面都已经涉及,就只沾题目了。
Milk Patterns G
公共串
Sandy 的卡片
结合并查集
品酒大会
即求后缀树组划分成连续若干个
我们发现若其为 “
不妨将
定义
对于两个区间
不妨定义
-
,防止负负得正的情况。 -
,可以理解一下。 -
之后就是基本的合并操作:
-
。 -
。 -
。 -
。
当然最开始的
是必须的。 -
最后统计答案即可。
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=3e5+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } int n,m,ans[N],sum[N],sa[N],rk[N],id[N],oldrk[N],cnt[N],key[N],height[N],a[N],f[N],r[N],mi[N],mx[N],siz[N],maxx[N]; char s[N]; void clean() { memset(rk,0,sizeof(rk)); memset(sa,0,sizeof(sa)); memset(id,0,sizeof(id)); memset(oldrk,0,sizeof(rk)); memset(cnt,0,sizeof(cnt)); memset(key,0,sizeof(key)); memset(height,0,sizeof(height)); } void count_sort(int n,int m) { memset(cnt,0,sizeof(cnt)); for(int i=1;i<=n;i++) cnt[key[i]]++; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) sa[cnt[key[i]]]=id[i], cnt[key[i]]--; } void init(char s[],int n) { int m=127,tot=0,num=0; for(int i=1;i<=n;i++) rk[i]=(int)s[i], id[i]=i, key[i]=rk[id[i]]; count_sort(n,m); for(int w=1;w<n&&tot!=n;w<<=1,m=tot) { num=0; for(int i=n;i>=n-w+1;i--) num++, id[num]=i; for(int i=1;i<=n;i++) if(sa[i]>w) num++, id[num]=sa[i]-w; for(int i=1;i<=n;i++) key[i]=rk[id[i]]; count_sort(n,m); for(int i=1;i<=n;i++) oldrk[i]=rk[i]; tot=0; for(int i=1;i<=n;i++) tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]), rk[sa[i]]=tot; } for(int i=1,k=0;i<=n;i++) { if(rk[i]==0) continue; if(k) k--; while(s[i+k]==s[sa[rk[i]-1]+k]) k++; height[rk[i]]=k; } } int find(int x) { return f[x]==x?x:f[x]=find(f[x]); } bool cmp(int a,int b) {return height[a]>height[b];} void merge(int x,int y,int z) { x=find(x),y=find(y); if(x==y) return ; maxx[x]=max({maxx[x],maxx[y],mx[x]*mx[y],mi[x]*mi[y]}); sum[z]+=siz[x]*siz[y]; ans[z]=max(ans[z],maxx[x]); mx[x]=max(mx[x],mx[y]); mi[x]=min(mi[x],mi[y]); f[y]=x; siz[x]+=siz[y]; } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n); cin>>s+1; init(s,n); memset(maxx,-0x3f,sizeof(maxx)); memset(ans,-0x3f,sizeof(ans)); for(int i=1;i<=n;i++) read(a[i]), r[i]=f[i]=i, siz[i]=1, mx[i]=mi[i]=a[i]; sort(r+1,r+1+n,cmp); for(int i=1;i<=n;i++) merge(sa[r[i]],sa[r[i]-1],height[r[i]]); for(int i=n-1;i>=0;i--) sum[i]+=sum[i+1], ans[i]=max(ans[i],ans[i+1]); for(int i=0;i<=n-1;i++) cout<<sum[i]<<' '<<(sum[i]!=0)*ans[i]<<endl; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效