1. 程式人生 > >4566: [Haoi2016]找相同字符——後綴自動機

4566: [Haoi2016]找相同字符——後綴自動機

getchar() rst 祖先 esp 匹配 pen .com turn pro

題目大意:

給定兩個串,求有多少種方式從兩個串中各提取出一個子串並且兩個子串相等。

思路:

涉及兩個串的子串問題考慮對第一個串建立SAM。
然後用第個二串在SAM上匹配,每到一個點,貢獻是(目前的長度-這個狀態的父親的長度)x這個狀態RIGHT集合的大小,同時對這個狀態的每個祖先也像這樣計算貢獻即可。

/*=======================================
 * Author : ylsoi
 * Time : 2019.2.14
 * Problem : bzoj4566
 * E-mail : [email protected]
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj4566.in","r",stdin);
    freopen("bzoj4566.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

const int maxn=2e5+10;
int n1,n2;
char s1[maxn],s2[maxn];

int len[maxn<<1],fa[maxn<<1],ch[maxn<<1][26];
int cnt=1,last=1,sz[maxn<<1];
ll sum[maxn<<1],ans;

void insert(int x){
    int p=last,np=last=++cnt;
    len[np]=len[p]+1;
    sz[np]=1;
    while(p && !ch[p][x])ch[p][x]=np,p=fa[p];
    if(!p)fa[np]=1;
    else{
        int q=ch[p][x];
        if(len[q]==len[p]+1)fa[np]=q;
        else{
            int nq=++cnt;
            memcpy(ch[nq],ch[q],sizeof(ch[nq]));
            len[nq]=len[p]+1,fa[nq]=fa[q];
            fa[q]=fa[np]=nq;
            while(p && ch[p][x]==q)ch[p][x]=nq,p=fa[p];
        }
    }
}

void get_sz(){
    int tax[maxn<<1]={0},lis[maxn<<1]={0};
    REP(i,1,cnt)++tax[len[i]];
    REP(i,1,n1)tax[i]+=tax[i-1];
    REP(i,1,cnt)lis[tax[len[i]]--]=i;
    DREP(i,cnt,1)sz[fa[lis[i]]]+=sz[lis[i]];

    REP(i,1,cnt)sum[i]=1ll*(len[i]-len[fa[i]])*sz[i];
    REP(i,1,cnt)sum[lis[i]]+=sum[fa[lis[i]]];
}

void compare(){
    int o=1,now=0;
    REP(i,1,n2){
        int x=s2[i]-'a';
        while(o!=1 && !ch[o][x])o=fa[o],now=len[o];
        if(ch[o][x]){
            o=ch[o][x];
            ++now;
            ans+=1ll*(now-len[fa[o]])*sz[o];
            ans+=sum[fa[o]];
        }
    }
}

int st[maxn],tp;

void dfs(int o){
    REP(i,1,tp)printf("%c",st[i]+'a');
    printf("\n");
    REP(i,0,25)if(ch[o][i]){
        st[++tp]=i;
        dfs(ch[o][i]);
        --tp;
    }
}

int main(){
    File();
    scanf("%s%s",s1+1,s2+1);
    n1=strlen(s1+1),n2=strlen(s2+1);
    REP(i,1,n1){
        insert(s1[i]-'a');
    }
    get_sz();
    compare();
    printf("%lld\n",ans);
    return 0;
}

4566: [Haoi2016]找相同字符——後綴自動機