[CF452E]Three strings

题目

传送门

题解

算法一

暴力做,枚举 \(A\) 的字串,在 \(B,C\) 中暴力找,时间复杂度 \(\mathcal O(n^4)\).

算法二

同样要在 \(A\) 中枚举字串,但是考虑在 \(L=1\) 时,对于 \(A\) 的每个字符,我们可以在 \(B,C\) 中找出相匹配的,随着 \(L\) 变大,可能的相同的部分也只可能在之前匹配的字符之后出现,所以我们可以考虑用类似于 vector 一类的东西将位置存下来,时间复杂度 \(\mathcal O(\tt{TLE})\).

算法三(SA)

考虑使用 \(\tt SA\) 将字符串匹配转换为 \(\tt LCP\) 问题.

首先,处理这种问题的关键是我们得将 \(A,B,C\) 首尾相接得到 \(S\),中间使用分隔符隔开,接下来我们称其为 \(S\)\(A,B,C\) 三个部分.

接下来,字符串内部的匹配问题就转化为后缀的 \(\tt LCP\) 问题,对于原来 \(A,B,C\) 三个串长度为 \(L\) 的匹配,现在即为属于三个不同部分的后缀的 \(\texttt{LCP} \ge L\) 即可.

但是,对于不同的部分,他们的 \(\tt LCP\) 并非是单调的,比如我们有个后缀排序的数组长这样

对于 \(L=4\) 时,可能有这些部分的 \(\tt LCP\) 是满足条件的

那么 \(\tt ans[4]\) 就是各个部分的 \(\tt cnt[A] \cdot cnt[B] \cdot cnt[C]\),其中 \(\tt cnt[i]\) 表示属于 \(i\) 部分的后缀的个数.

但是,随着 \(L\) 变大,各个部分有可能会断开,这让我们非常不好处理,既然 \(L\) 变大会断开,那么同样意味着,如果我们倒着处理 \(L\),那么随着 \(L\) 的变小,会有越来越多的部分连接在一起,这个时候我们只需要维护 cnt[i] 即可,而这个部分可以使用并查集来做,并查集合并的时候,我们也可以维护答案.

关键代码:

const int jzm=1000000007;
/** @brief 全局答案*/
int sy=0;
/** @brief 对应长度的答案*/
int ans[maxn+5];
struct node{
    // x 是后缀编号
    int x,v;
    inline int operator <(const node rhs)const{
        return v>rhs.v;
    }
}v[maxn+5+5];
int fa[maxn+5];
int find(const int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int cock(const int x){
    return 1ll*tot[x][1]*tot[x][2]%jzm*tot[x][3]%jzm;
}
inline void merge(int x,int y){
    // printf("merge:> x == %d, y == %d\n",x,y);
    x=find(x),y=find(y);
    if(x==y)return;
    sy-=cock(x);if(sy<0)sy+=jzm;
    sy-=cock(y);if(sy<0)sy+=jzm;
    fa[x]=y;
    rep(j,1,3)tot[y][j]+=tot[x][j];
    sy+=cock(y);
    if(sy>=jzm)sy-=jzm;
}

inline void solve(){
    rep(i,1,n)v[i]=node{sa[i],heit[i]};
    rep(i,1,n)fa[i]=i;
    sort(v+1,v+n+1);
    // rep(i,1,n)printf("v[%d] : x == %d, v == %d\n",i,v[i].x,v[i].v);
    int now=1;
    fep(i,min_len,1){
        while(now<=n && v[now].v>=i){
            // printf("When i == %d, now == %d\n",i,now);
            int pre=sa[rk[v[now].x]-1];
            merge(pre,v[now].x);
            ++now;
        }
        ans[i]=sy;
    }
    rep(i,1,min_len)printf("%d ",ans[i]);
}

算法四(SAM)

广义自动机板题.

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
namespace Elaina{
    #define rep(i,l,r) for(int i=l,i##_end_=r;i<=i##_end_;++i)
    #define fep(i,l,r) for(int i=l,i##_end_=r;i>=i##_end_;--i)
    #define fi first
    #define se second
    #define Endl putchar('\n')
    #define writc(x, c) fwrit(x),putchar(c)
    // # define int long long
    typedef long long ll;
    typedef pair<int,int> pii;
    typedef unsigned long long ull;
    typedef unsigned int uint;
    template<class T>inline T Max(const T x,const T y){return x<y?y:x;}
    template<class T>inline T Min(const T x, const T y){return x<y?x:y;}
    template<class T>inline T fab(const T x){return x<0?-x:x;}
    template<class T>inline void getMax(T& x, const T y){x=Max(x,y);}
    template<class T>inline void getMin(T& x, const T y){x=Min(x,y);}
    template<class T>T gcd(const T x,const T y){return y?gcd(y,x%y):x;}
    template<class T>inline T readin(T x){
        x=0;int f = 0;char c;
        while((c=getchar())<'0'||'9'<c)if(c=='-')f=1;
        for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
        return f?-x:x;
    }
    template<class T>void fwrit(const T x){
        if(x<0)return putchar('-'),fwrit(-x);
        if(x>9)fwrit(x/10);putchar(x%10^48);
    }
}
using namespace Elaina;

const int maxn=3e5;
const int mod=1e9+7;

int trie[maxn*2+5][26];
int fa[maxn*2+5],len[maxn*2+5];
int cnt=1,lst;
int sz[maxn*2+5][3];
inline void add(const int c,const int col){
    // printf("add :> c == %d, col == %d\n",c,col);
    int p=lst,u,q,nq;
    if(trie[p][c]){
        q=trie[p][c];
        if(len[q]==len[p]+1)lst=q;
        else{
            int nq=lst=++cnt;
            rep(i,0,25)trie[nq][i]=trie[q][i];
            fa[nq]=fa[q],len[nq]=len[p]+1;
            fa[q]=nq;
            for(;p&&trie[p][c]==q;p=fa[p])trie[p][c]=nq;
        }
        ++sz[lst][col];
        return;
    }
    u=lst=++cnt;
    len[u]=len[p]+1,++sz[u][col];
    for(;p&&!trie[p][c];p=fa[p])trie[p][c]=u;
    if(!p)fa[u]=1;
    else{
        q=trie[p][c];
        if(len[q]==len[p]+1)fa[u]=q;
        else{
            nq=++cnt;
            rep(i,0,25)trie[nq][i]=trie[q][i];
            fa[nq]=fa[q],len[nq]=len[p]+1;
            fa[q]=fa[u]=nq;
            for(;p&&trie[p][c]==q;p=fa[p])trie[p][c]=nq;
        }
    }
}

char s[maxn+5];

int n,ans[maxn+5];

struct edge{int to,nxt;
    edge(const int T=0,const int N=0):to(T),nxt(N){}
}e[maxn*2+5];
int tail[maxn*2+5],ecnt;
inline void add_edge(const int u,const int v){
    // printf("add_edge :> u == %d, v == %d\n",u,v);
    e[++ecnt]=edge(v,tail[u]);tail[u]=ecnt;
}
void dfs(const int u){
    for(int i=tail[u],v;i;i=e[i].nxt){
        dfs(v=e[i].to);
        rep(j,0,2)sz[u][j]+=sz[v][j];
    }
    // printf("node %d :>\n",u);
    // rep(i,0,2)printf("sz[%d][%d] == %d\n",u,i,sz[u][i]);
    (ans[len[fa[u]]+1]+=1ll*sz[u][0]*sz[u][1]%mod*sz[u][2]%mod)%=mod;
    (ans[len[u]+1]+=mod-1ll*sz[u][0]*sz[u][1]%mod*sz[u][2]%mod)%=mod;
}

signed main(){
    n=maxn+5;
    rep(t,0,2){
        scanf("%s",s+1);lst=1;
        // puts(s+1);
        int len=strlen(s+1);n=Min(n,len);
        rep(i,1,len)add(s[i]-'a',t);
    }
    rep(i,2,cnt)add_edge(fa[i],i);
    dfs(1);
    /*
    rep(i,1,cnt)++c[len[i]];
    rep(i,1,n)c[i]+=c[i-1];
    fep(i,cnt,1)a[len[i]--]=i;
    fep(i,cnt,2)rep(j,0,2)
        sz[fa[a[i]]][j]+=sz[a[i]][j];
    rep(i,2,cnt){
        ans[len[i]]+=1ll*sz[i][0]*sz[i][1]%mod*sz[i][2]%mod;
        ans[len[i]]%=mod;
    }
    */
    rep(i,1,n)ans[i]=(ans[i]+ans[i-1])%mod;
    rep(i,1,n)writc(ans[i],' ');
    return 0;
}

一道类似的题

传送门

posted @ 2020-12-23 17:35  Arextre  阅读(81)  评论(0编辑  收藏  举报