BZOJ4566 [Haoi2016]找相同字符 SAM+拓扑

学了 几天 SAM 这道题总算是领会到了些许 对 parent 树有了更深的理解 但是我还是不能表述出来 太菜了

大家可以去百度CLJ神犇的PPT 讲的很好 附一个百度文库的网址

我们可以先跑出两个串的 SAM 对于每一个节点 记录到达此节点的串的数目 这时候要用到拓扑 因为对于一个节点 i 如果有两个串能在i匹配 则他一定能下parent[i] 匹配 所以到拓扑将每一个节点的匹配数传给 parent 然后因为每一个节点是有 len 的 所以每个节点的个数应该为 (len[i]len[parent[i]])sz[i][0]sz[i][1] 求和就好啦

#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=8e5+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;
}
char s[N]; 
ll ans;
int deg[N],par[N],siz[2][N],ch[N][26],len[N],n,root,sz,last;
void extend(int x){
    int p=last,np=++sz;
    len[np]=len[p]+1;
    for(;p&&!ch[p][x];p=par[p]) ch[p][x]=np;
    if(!p) par[np]=root;
    else{
        int q=ch[p][x];
        if(len[q]==len[p]+1) par[np]=q;
        else{
            int nq=++sz;
            len[nq]=len[p]+1;
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            par[nq]=par[q];par[q]=par[np]=nq;
            for(;p&&ch[p][x]==q;p=par[p])ch[p][x]=nq;
        }
    }
//  cout<<np<<" "<<par[np]<<endl; 
    last=np;
}
void topsort(){
    for(int i=1;i<=sz;i++) deg[par[i]]++;
    queue<int>q;
    for(int i=1;i<=sz;i++) if(!deg[i]) q.push(i);
    while(!q.empty()){
        int u=q.front();q.pop();
        siz[0][par[u]]+=siz[0][u];
        siz[1][par[u]]+=siz[1][u];
//      bug(siz[0][u]),bug(siz[1][u]);
        deg[par[u]]--;
        if(!deg[par[u]]) q.push(par[u]);
    }
}
int main(){
#ifdef Devil_Gary
    freopen("in.txt","r",stdin);
#endif
    scanf("%s",s+1);
    n=strlen(s+1);
    last=sz=root=1;
    int p=root;
    for(int i=1;i<=n;i++){
        extend(s[i]-'a');
        p=ch[p][s[i]-'a'];
        siz[0][p]++;
    } 
    scanf("%s",s+1);
    n=strlen(s+1);
    last=root;p=root; 
    for(int i=1;i<=n;i++){
        extend(s[i]-'a');
        p=ch[p][s[i]-'a'];
        siz[1][p]++;
    } 
    topsort();
    for(int i=1;i<=sz;i++) if(par[i]){
//      cout<<siz[1][i]<<" "<<siz[0][i]<<endl; 
        ans+=(ll)(len[i]-len[par[i]])*siz[0][i]*siz[1][i];
    }
    return printf("%lld\n",ans),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值