Codeforces427 D. Match & Catch(只出现一次的最短公共子串,在两个后缀自动机上dfs,同时转移)

题意:

给定字符串s和t,要求找到s和t的最短公共子串,满足该子串在s和t中都只出现一次
如果不存在公共子串则输出-1,否则输出最短长度。

数据范围:|s|,|t|<=5e3

解法:

对s和t都建立SAM,标记end,在parent树上树形dp计算出sz[]

然后定义两个遍历,分别从两个SAM的根开始dfs,转移的时候同时转移(转移字符相同,这样保证串相同)。
假设当前SAM(s)上遍历到x,SAM(t)上遍历到y,如果SAM(s).sz[x]==1且SAM(t).sz[y]==1则更新答案。

没看懂的话可以直接看代码,代码还蛮简单的。

code:

#include
using namespace std;
#define ll long long
const int maxm=2e6+5;
char s[maxm];
char t[maxm];
int ans=1e9;
struct SAM{
    int ch[maxm][26];
    int fa[maxm],l[maxm];//l[]是等价类的最长字符串长度len
    int last=1,tot=1;//tot是节点数量
    //本题要用的
    int sz[maxm];
    int head[maxm],nt[maxm],to[maxm],cnt;
    void add(int x,int y){
        cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;
    }
    void dfs(int x){
        for(int i=head[x];i;i=nt[i]){
            int v=to[i];
            dfs(v);
            sz[x]+=sz[v];
        }
    }
    void cal_sz(){//计算sz[x]
        for(int i=2;i<=tot;i++)add(fa[i],i);
        dfs(1);
    }
    //SAM构造
    void add(int c){
        int p=last,np=++tot;last=np;l[np]=l[p]+1;
        for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
        if(!p)fa[np]=1;
        else{
            int q=ch[p][c];
            if(l[p]+1==l[q])fa[np]=q;
            else{
                int nq=++tot;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof ch[q]);
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
            }
        }
        sz[np]=1;//标记
    }
}S[2];
void dfs(int x,int y,int len){
    if(len>ans)return ;
    if(len&&S[0].sz[x]==1&&S[1].sz[y]==1){
        ans=min(ans,len);
        return ;
    }
    for(int i=0;i<26;i++){
        if(S[0].ch[x][i]&&S[1].ch[y][i]){
            dfs(S[0].ch[x][i],S[1].ch[y][i],len+1);
        }
    }
}
signed main(){
    //
    scanf("%s",s+1);
    int n=strlen(s+1);
    for(int i=1;i<=n;i++){
        S[0].add(s[i]-'a');
    }
    S[0].cal_sz();
    //
    scanf("%s",t+1);
    int m=strlen(t+1);
    for(int i=1;i<=m;i++){
        S[1].add(t[i]-'a');
    }
    S[1].cal_sz();
    //
    dfs(1,1,0);
    if(ans==1e9)ans=-1;
    cout<<ans<<endl;
    return 0;
}

你可能感兴趣的:(Codeforces427 D. Match & Catch(只出现一次的最短公共子串,在两个后缀自动机上dfs,同时转移))