bzoj3277: 串
总的来讲,SAM难想,SA难写,SAM跑得快
SAM:
这题其实巨像bzoj2780
建了广义SAM以后,用dfs序+树状数组求出每个点在多少不同的串里面
然后枚举每个串在上面跑就行了。
SA:
然后如果要上后缀数组的话,把它们接在一起弄,中间插不同的乱七八糟的符号,先套路一手st表求LCP
对于每个字符串枚举他的后缀,考虑每个后缀的前缀对答案的贡献,明显假如第2个前缀可以,第1个前缀也是可以的
那我们可以二分这个前缀长度,找到满足LCP>=mid的前后最远位置ll、rr,然后去check是否包含了超过k种颜色
check的方法:我们可以预处理出一个数组L,L[i]表示字典序排名第i的后缀,往前到L[i]才能够包含超过k种颜色,然后如果L[rr]>=ll就是可行的。
这样的复杂度是nlog^2n的,但其实可以把二分前缀给省掉
回忆SA height数组的求法,我们有height[i-1]<=height[i]-1,如果令第i个前缀的贡献为F[i],是否也满足F[i-1]<=F[i]-1呢?不难发现答案是肯定的,于是我们就可以用与求height数组类似的写法,达到nlogn的复杂度(虽然枚举不是严格的n。。。。)
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<vector> using namespace std; struct SAM { int w[30],dep,c,fail; }ch[410000];int cnt,las; void add(int dep,int x,int c) { int now=++cnt,pre=las; ch[now].dep=dep;ch[now].c=c; las=now; while(pre!=0&&ch[pre].w[x]==0) ch[pre].w[x]=now, pre=ch[pre].fail; if(pre==0)ch[now].fail=1; else { int nxt=ch[pre].w[x]; if(ch[nxt].dep==ch[pre].dep+1)ch[now].fail=nxt; else { int nnxt=++cnt; ch[nnxt]=ch[nxt],ch[nnxt].dep=ch[pre].dep+1; ch[now].fail=ch[nxt].fail=nnxt; while(pre!=0&&ch[pre].w[x]==nxt) ch[pre].w[x]=nnxt, pre=ch[pre].fail; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ struct query{int l,r,id;}q[410000];int qlen,RR; bool cmp(query q1,query q2){return q1.r==q2.r?q1.l<q2.l:q1.r<q2.r;} struct node { int x,y,next; }a[1100000];int len,last[410000]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int z,ys[410000],cc[410000]; void dfs(int x) { ys[x]=++z;cc[z]=ch[x].c; for(int k=last[x];k;k=a[k].next)dfs(a[k].y); qlen++; q[qlen].l=ys[x],q[qlen].r=z,q[qlen].id=x; RR=max(RR,q[qlen].r); } void MakeFailTree() { for(int i=2;i<=cnt;i++)ins(ch[i].fail,i); dfs(1); } //----------------------------------------------- SAM && make fail tree--------------------------------------------------------------- int s[410000]; int lowbit(int x){return x&-x;} void change(int x,int k){ while(x<=cnt+10){s[x]+=k;x+=lowbit(x);} } int getsum(int x){ int ret=0; while(x>0){ret+=s[x];x-=lowbit(x);} return ret; } //.............bit................. int cla[410000],num[410000]; void calc()//计算:每棵子树中的颜色数==当前节点管理的子串能够被多少串表示 { sort(q+1,q+qlen+1,cmp); int tp=1; for(int i=1;i<=RR;i++) { if(cla[cc[i]]!=0) change(cla[cc[i]],-1); cla[cc[i]]=i; change(cla[cc[i]],1); while(tp<=qlen&&q[tp].r==i) { num[q[tp].id]=getsum(q[tp].r)-getsum(q[tp].l-1); tp++; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ int K,b[410000],sum[410000];//当前位置是否满足条件,我的前缀有多少满足条件(不包括自己) void godfs(int x) { if(num[x]>=K)b[x]=1; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; sum[y]=sum[x]+b[x]*(ch[x].dep-ch[ch[x].fail].dep); godfs(y); } } //---------------------------------------------------------------------------------------------------------------------------------- vector<int>vec[110000]; int work(int o) { int now=1,L=0,ans=0; for(int i=0;i<vec[o].size();i++) { int x=vec[o][i]; while(now!=0&&ch[now].w[x]==0) now=ch[now].fail, L=ch[now].dep; if(now==0) now=1, L=0; else { now=ch[now].w[x],L++; ans+=sum[now]+b[now]*(L-ch[ch[now].fail].dep); } } return ans; } //--------------------------------------------------------------------------------------------------------------------------------- char ss[110000];int sslen; int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int n; scanf("%d%d",&n,&K); cnt=1; for(int i=1;i<=n;i++) { scanf("%s",ss+1),sslen=strlen(ss+1); las=1; for(int j=1;j<=sslen;j++) { add(j,ss[j]-'a'+1,i),vec[i].push_back(ss[j]-'a'+1); } } z=qlen=RR=0; MakeFailTree(),calc(); godfs(1); for(int i=1;i<=n;i++) printf("%d ",work(i)); return 0; }
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int ss[210000]; int sa1[210000],Rank[210000]; int sa2[210000],Rsort[210000],tt[210000]; void get_sa(int n,int m) { for(int i=1;i<=n;i++)Rank[i]=ss[i]; memset(Rsort,0,sizeof(Rsort)); for(int i=1;i<=n;i++)Rsort[Rank[i]]++; for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1]; for(int i=n;i>=1;i--)sa1[Rsort[Rank[i]]--]=i; int ln=1,p=0; while(p<n) { int k=0;for(int i=n-ln+1;i<=n;i++)sa2[++k]=i; for(int i=1;i<=n;i++) if(sa1[i]-ln>0)sa2[++k]=sa1[i]-ln; memset(Rsort,0,sizeof(Rsort)); for(int i=1;i<=n;i++)Rsort[Rank[sa2[i]]]++; for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1]; for(int i=n;i>=1;i--)sa1[Rsort[Rank[sa2[i]]]--]=sa2[i]; memcpy(tt,Rank,sizeof(tt)); p=1;Rank[sa1[1]]=p; for(int i=2;i<=n;i++) { if(tt[sa1[i]]!=tt[sa1[i-1]]||tt[sa1[i]+ln]!=tt[sa1[i-1]+ln])p++; Rank[sa1[i]]=p; } ln*=2;m=p; } } int height[210000]; void get_he(int n) { int h=0; for(int i=1;i<=n;i++) { int j=sa1[Rank[i]-1]; if(h!=0)h--; while(ss[i+h]==ss[j+h])h++; height[Rank[i]]=h; } } //--------------------------------------------------sa------------------------------------------------------------- int Bin[30],Log[210000]; int f[30][210000],d[210000]; void get_st(int n) { Bin[0]=1;for(int i=1;i<=25;i++)Bin[i]=Bin[i-1]*2; Log[1]=0;for(int i=2;i<=n ;i++)Log[i]=Log[i/2]+1; memset(f,63,sizeof(f)); for(int i=0;i<=n;i++)f[0][i]=height[i]; for(int j=1;j<=25;j++) for(int i=1;i+Bin[j]-1<=n;i++) f[j][i]=min(f[j-1][i],f[j-1][i+Bin[j-1]]); } int getLCP(int l,int r) { l++;if(l>r)return (1<<30); int k=Log[r-l+1]; return min(f[k][l],f[k][r-Bin[k]+1]); } //------------------------------------------------套路st表--------------------------------------------------------- int K,L[210000],b[210000],c[210000]; void getL(int N,int n) { int l=N+1,s=0; for(int r=N+1;r<=n;r++) { c[b[sa1[r]]]++; if(c[b[sa1[r]]]==1)s++; bool flag=false; while(l<r&&s>=K) { flag=true; c[b[sa1[l]]]--; if(c[b[sa1[l]]]==0)s--; l++; } if(flag) { l--; c[b[sa1[l]]]++; if(c[b[sa1[l]]]==1)s++; } if(s>=K)L[r]=l; } } //---------------------------------------------------------------------------------------------------------- bool check(int x,int p,int n) { int l,r,ll,rr; l=1,r=x-1,ll=x; while(l<=r) { int mid=(l+r)/2; if(getLCP(mid,x)>=p) { ll=mid; r=mid-1; } else l=mid+1; } l=x+1,r=n,rr=x; while(l<=r) { int mid=(l+r)/2; if(getLCP(x,mid)>=p) { rr=mid; l=mid+1; } else r=mid-1; } if(ll<=L[rr])return true; else return false; } int OL[210000],OR[210000]; int solve(int o,int n) { int p=0,ans=0; for(int i=OL[o];i<=OR[o];i++) { if(p!=0)p--; while(check(Rank[i],p,n)==true)p++; p--; ans+=p; } return ans; } char sc[210000]; int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int n; scanf("%d%d",&n,&K); int sslen=0; for(int i=1;i<=n;i++) { scanf("%s",sc+1);int sclen=strlen(sc+1); OL[i]=sslen+1; for(int j=1;j<=sclen;j++) ss[++sslen]=n+sc[j]-'a'+1,b[sslen]=i,d[sslen]=sclen-j+1; OR[i]=sslen; ss[++sslen]=i; } get_sa(sslen,120000),get_he(sslen); get_st(sslen); getL(n,sslen); for(int i=1;i<=n;i++); for(int i=1;i<=n;i++) printf("%d ",solve(i,sslen)); return 0; }
pain and happy in the cruel world.