BZOJ3473 字符串 广义后缀自动机

今天主攻了下SAM 好多东西以前都没理解到
对于这道题 我们建一个自动机存所有串 每个穿last从1开始
对于自动机上每个点额外记一个cnt 表示能匹配到这个点的不同串个数
建完对每个串在自动机上匹配 把到的每个点x和par[x],par[par[x]]…的cnt++
然后就从父亲往儿子传递一下 这样每个点i就存了所有 len[i]的数量 这里我用的拓扑序转移
最后再对每个串在自动机上跑一遍 统计答案就好啦~

#include<bits/stdc++.h>
#define bug(x) cout<<(#x)<<" "<<(x)<<endl
#define ll long long
/*
char *TT,*mo,but[(1<<15)+2];
#define getchar() ((TT==mo&&(mo=(TT=but)+fread(but,1,1<<15,stdin),TT==mo))?-1:*TT++)//*/
using namespace std;
const int N=2e5+5;
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;
}
ll ans[N];
int n,k,amaz,tot,rt,last,cnt[N],ch[N][27],len[N],par[N],l[N],tmp[N],sum[N],dig[N],vis[N];
string a[N];
void extend(int x){
    int p=last,np=++tot;
    last=np,len[np]=len[p]+1;
    for(;p&&!ch[p][x];p=par[p]) ch[p][x]=np;
    if(!p) par[np]=rt;
    else{
        int q=ch[p][x];
        if(len[q]==len[p]+1) par[np]=q;
        else{
            int nq=++tot;
            memcpy(ch[nq],ch[q],sizeof ch[q]);
            len[nq]=len[p]+1;
            par[nq]=par[q];par[q]=par[np]=nq;
            for(;p&&ch[p][x]==q;p=par[p]) ch[p][x]=nq;
        }
    }
}
void top(){
    queue<int>q;
    for(int i=1;i<=tot;i++) if(!dig[i]) q.push(i),tmp[++amaz]=i;
    while(!q.empty()){
        int x=q.front();q.pop();
        if(!--dig[par[x]]) {
            q.push(par[x]),tmp[++amaz]=par[x];
        }
    }
    for(int i=amaz;i;i--) sum[tmp[i]]+=sum[par[tmp[i]]];
}
int main(){
#ifdef Devil_Gary
    freopen("in.txt","r",stdin);
#endif
    n=read(),k=read();
    rt=tot=last=1;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        l[i]=a[i].length();
        last=1;
        for(int j=0;j<l[i];j++) extend(a[i][j]-'a');
    }
    for(int j=1;j<=n;j++){
        int p=1;
        for(int i=0;i<l[j];i++){
            p=ch[p][a[j][i]-'a'];
            int x=p;
            while(x&&vis[x]!=j) {
                cnt[x]++,vis[x]=j,x=par[x];
            }
        }
    }
    for(int i=1;i<=tot;i++) dig[par[i]]++,sum[i]=(cnt[i]>=k)?(len[i]-len[par[i]]):0;
    top();
    for(int j=1;j<=n;j++){
        int p=1;
        for(int i=0;i<l[j];i++){
            p=ch[p][a[j][i]-'a'];
            ans[j]+=sum[p];
        }
        printf("%lld ",ans[j]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值