CF1483F
没有发现 SA 做法于是来发一篇。
我们考虑大串对小串的贡献,从左往右扫,拿当前扫到的点作为左端点,那么最远的能作为子串的右端点才有可能对答案有贡献。
并且我们发现,作为子串的右端点应当是递增的,否则会出现包含关系。
那么一个小串在大串中作为右端点的次数与在大串中出现次数相等时,就对答案有 1 的贡献。
到这里已经可以做出本题了,大概做法就是套路地把所有串拼在一起进行后缀排序,二分出一个串被包含的后缀排名区间进行区间取最大值,然后对每个串进行一个扫,维护一个右端点指针统计贡献即可,空间复杂度 \(O(n)\),时间复杂度 \(O(n\log n)\)。
但是我们发现复杂度还有优化空间,使用 \(SA-IS\) 进行后缀排序,然后桶排后使用并查集求出一个串被包含的后缀排名区间并区间取最大值,然后离线差分出需要的小串在大串中的出现次数,这样的话如果把并查集复杂度视作常数,时间复杂度就被优化到了 \(O(n)\)。
总复杂度是可以做到线性的,尽管毫无实际意义。
代码是第一种做法。感觉比其他做法代码长多了。
Code
#include<vector>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
namespace EMT{
typedef long long ll;typedef double db;
#define pf printf
#define F(i,a,b) for(int i=a;i<=b;i++)
#define D(i,a,b) for(int i=a;i>=b;i--)
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
inline void pi(int x){pf("%d ",x);}inline void pn(){pf("\n");}
const int N=2e6+10;
int s[N],_='z',n,beg[N],fa[N],vl[N],vr[N],wl[N],wr[N],len,Len[N],fa2[N],val[N];char ss[N];
namespace SA{
int b[N],sa[N],rk[N],height[N],Emilia[N],Arknights[N],n,m,num;
inline void getsa(){
int *x=Emilia,*y=Arknights;
F(i,1,n)b[x[i]=s[i]]++;m=_;
F(i,1,m)b[i]+=b[i-1];
D(i,n,1)sa[b[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
num=0;
F(i,n-k+1,n)y[++num]=i;
F(i,1,n)if(sa[i]>k)y[++num]=sa[i]-k;
memset(b,0,sizeof(int)*(m+1));
F(i,1,n)b[x[i]]++;
F(i,1,m)b[i]+=b[i-1];
D(i,n,1)sa[b[x[y[i]]]--]=y[i],y[i]=0;
std::swap(x,y);
x[sa[num=1]]=1;
F(i,2,n)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
if(n==num)break;
m=num;
}
}
inline void getheight(){
int k=0;
F(i,1,n)rk[sa[i]]=i;
F(i,1,n)if(rk[i]>1){
if(k)k--;int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++;
height[rk[i]]=k;
}
}
inline void init(){
getsa(),getheight();
F(i,1,n+1)fa[i]=fa2[i]=vl[i]=vr[i]=i;
}
}using SA::rk;using SA::height;
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y){
x=find(x),y=find(y);
vl[y]=min(vl[y],vl[x]),vr[y]=max(vr[y],vr[x]);
fa[x]=y;
}
inline int find2(int x){return fa2[x]==x?x:fa2[x]=find2(fa2[x]);}
std::vector<int>in[N];
inline int ask(int x,int y){return std::upper_bound(in[y].begin(),in[y].end(),wr[x])-std::lower_bound(in[y].begin(),in[y].end(),wl[x]);}
int ans=0;
inline void solve(int x){
static int cnt[N];static bool vis[N];
int now=0;
F(i,beg[x],beg[x]+Len[x]-1){
if(!val[rk[i]]||Len[val[rk[i]]]+i-1<=now)continue;
now=i+Len[val[rk[i]]]-1,cnt[val[rk[i]]]++;
}now=0;
F(i,beg[x],beg[x]+Len[x]-1){
if(!val[rk[i]]||Len[val[rk[i]]]+i-1<=now)continue;
now=i+Len[val[rk[i]]]-1;
if(!vis[val[rk[i]]]&&cnt[val[rk[i]]]==ask(val[rk[i]],x))ans++,vis[val[rk[i]]]=1;
}now=0;
F(i,beg[x],beg[x]+Len[x]-1){
if(!val[rk[i]]||Len[val[rk[i]]]+i-1<=now)continue;
now=i+Len[val[rk[i]]]-1;
cnt[val[rk[i]]]=vis[val[rk[i]]]=0;
}
}
int Emilia[N],*b;
inline short main(){
n=read();int mx=0;
F(i,1,n){
scanf("%s",ss+1);
Len[i]=strlen(ss+1);
beg[i]=len+1;mx=max(mx,Len[i]);
F(j,1,Len[i])s[++len]=ss[j];
s[++len]=++_;
}SA::n=len;SA::init();
static int p1[N],p2[N];
b=Emilia+1;
F(i,2,len)b[height[i]]++;
D(i,len,0)b[i]+=b[i+1];
F(i,2,len)p1[b[height[i]]--]=i;
memset(b,0,sizeof(int)*(len+1));
F(i,1,n)b[Len[i]]++;
D(i,mx,1)b[i]+=b[i+1];
F(i,1,n)p2[b[Len[i]]--]=i;
F(i,1,n){
F(j,beg[i],beg[i]+Len[i]-1)
in[i].push_back(rk[j]);
std::sort(in[i].begin(),in[i].end());
}
int j=1;
F(i,1,n){
const int v=p2[i];
while(j<len&&height[p1[j]]>=Len[v])merge(p1[j]-1,p1[j]),j++;
const int fa=find(rk[beg[v]]);
wl[v]=vl[fa],wr[v]=vr[fa];
for(int x=find2(vl[fa]);x<rk[beg[v]];x=find2(x)){
val[x]=v;fa2[find2(x)]=find2(x+1);
}
for(int x=find2(rk[beg[v]]+1);x<=vr[fa];x=find2(x)){
val[x]=v,fa2[find2(x)]=find2(x+1);
}
}
F(i,1,n)solve(i);
pi(ans);
return 0;
}
}
signed main(){return EMT::main();}
Everything that kills me makes me feel alive.