UVALive 7902 后缀自动机

题意

给N个字符串,找出第一个字符串中最短的一个子串,使得这个子串不在其他所有的字符串中出现。

题解

对于这样一道题,没有现成的工具可以利用,但是既然涉及到了子串的问题,就可以考虑后缀自动机。后缀自动机可以用来找最长公共子串。对于这道题,我们想要利用后缀自动机,就是需要采取一些措施,使得这个问题转化为最长公共子串的问题。
我们可以把其他字符串拼接成一个字符串,需要注意的是要在中间加上一个分割字符,因为如果没有这个字符的话,那么很有可能这个字符串与下一个字符串拼接成一个新的字符串,从而对结果产生影响。
拼接成一个字符串以后,把这个字符串加到后缀自动机上,就可以寻找最长公共子串了,在寻找最长公共子串的过程中,如果发生了失配,那么就意味着存在一段子串,这段子串不在任何一个字符串中存在,而这个字符串的长度就是当前匹配的最长公共子串长度-1。(这个原因是显而易见的,因为匹配失败就意味着不存在这个字串在其他字符串中。)这时候我们就需要比较一下字典序,进行答案的记录。
最后输出记录的位置对应的字符串就可以了。

代码

#include<bits/stdc++.h>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define W(t) while(t)
#define MEM(a,b) memset(a,b,sizeof(a))
#define LL long long
#define INF 0x3f3f3f3f
#define eps 1e-10
#define MAXN 3000010
using namespace std;
const int N=250010;
char first[N];
char second[N];
struct Node {
    int st,ed;
    Node() {}
    Node(int st,int ed):st(st),ed(ed) {}
    bool operator < (const Node b) const {
        int lena=(ed-st+1),lenb=(b.ed-b.st+1);
        if(lena==lenb) {
            UP(i,0,lena) {
                if(first[st+i]!=first[b.st+i]) {
                    return first[st+i]<first[b.st+i];
                }
            }
            return true;
        }
        return lena<lenb;
    }
};
Node ans;
struct Suffix_Auto {
    static const int NODE=N<<1,C=27;
    int allc,last,par[NODE],len[NODE],trans[NODE][C];
    int newNode() {
        int ret=++allc;
        MEM(trans[ret],0);
        return  ret;
    }
    void init() {
        allc=0;
        last=newNode();
        par[last]=len[last]=0;
    }
    void extend(int c) {
        int p=last,np=newNode();
        len[np]=len[last]+1;
        for(; p&&!trans[p][c]; p=par[p]) trans[p][c]=np;
        if(!p) par[np]=1;
        else {
            int q=trans[p][c];
            if(len[q]==len[p]+1) par[np]=q;
            else {
                int nq=++allc;
                par[nq]=par[q];
                len[nq]=len[p]+1;
                memcpy(trans[nq],trans[q],C<<2);
                par[np]=par[q]=nq;
                for(trans[p][c]=nq,p=par[p]; p&&trans[p][c]==q; p=par[p]) trans[p][c]=nq;
            }
        }
        last=np;
    }
    void update(Node nd) {
        if(ans.st==-1) {
            ans=nd;
        } else if(nd<ans) {
            ans=nd;
        }
    }
    void find(const char *st) {
        int stlen=strlen(st);
        int u=1;
        int now=0;
        UP(i,0,stlen) {
            int x=st[i]-'a';
            if(trans[u][x]) {
                u=trans[u][x];
                now++;
            } else {
                W(u&&!trans[u][x]) {
                    u=par[u];
                    now=len[u];
                }
                if(u) {
                    now++;
                    u=trans[u][x];
                } else {
                    now=0;
                    u=1;
                }
                Node newNd=Node(i-now,i);
                update(newNd);
            }
        }
    }
};

Suffix_Auto sa;
int main() {
//    freopen("d://in.txt","r",stdin);
//    freopen("d://out1.txt","w",stdout);
    int ks=1;
    int t;
    scanf("%d",&t);
    W(t--) {
        sa.init();
        int n;
        scanf("%d",&n);
        UP(i,0,n) {
            if(i==0)
                scanf("%s",first);
            else {
                scanf("%s",second);
                int len=strlen(second);
                UP(i,0,len) sa.extend(second[i]-'a');
                sa.extend(26);
            }
        }
        ans.st=-1;
        sa.find(first);
        printf("Case #%d: ",ks++);
        if(ans.st==-1) {
            printf("Impossible\n");
        } else {
            UP(i,ans.st,ans.ed+1) printf("%c",first[i]);
            puts("");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值