BJ模拟 Problem B: Mr. Panda and Fantastic Beats【广义SAM】

本文介绍了一种使用广义后缀自动机(SAM)解决寻找字符串中最短的、仅属于第一个字符串的子串的问题的方法。通过构建广义SAM并利用贪心策略,该算法能在较短时间内找到满足条件的最短子串。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

给出 nn 个字符串 S1,S2,...Sn,求S1S1中最短的子串满足其不是S2,...,SnS2,...,Sn的子串。字符串总长不超过250000。

解题思路:

将所有串建立广义SAM,维护每个点id的最大值即可判断其是否唯一是 S1S1 的子串,然后贪心找合法串即可。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
ll getint()
{
    ll i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=500005;
int n,tot,last,fa[N],son[N][26],id[N],mx[N],len[N],c[N],q[N];
char s[N];
void clear()
{
    for(int i=1;i<=tot;i++)
    {
        memset(son[i],0,sizeof(son[i]));
        mx[i]=fa[i]=id[i]=len[i]=0;
    }tot=1;
}
void extend(int c)
{
    int p=last;
    if(son[p][c])
    {
        int q=son[p][c];
        if(mx[q]==mx[p]+1)last=q;
        else
        {
            int nq=last=++tot;mx[nq]=mx[p]+1;
            memcpy(son[nq],son[q],sizeof(son[q])),fa[nq]=fa[q],fa[q]=nq;
            while(p&&son[p][c]==q)son[p][c]=nq,p=fa[p];
        }
    }
    else
    {
        int np=last=++tot;mx[np]=mx[p]+1;
        while(p&&!son[p][c])son[p][c]=np,p=fa[p];
        if(!p)fa[np]=1;
        else
        {
            int q=son[p][c];
            if(mx[q]==mx[p]+1)fa[np]=q;
            else
            {
                int nq=++tot;mx[nq]=mx[p]+1;
                memcpy(son[nq],son[q],sizeof(son[q])),fa[nq]=fa[q],fa[q]=fa[np]=nq;
                while(p&&son[p][c]==q)son[p][c]=nq,p=fa[p];
            }
        }
    }
}
void W(int p)
{
    if(len[p]==1)return;
    int j=26,ml=1e9;
    for(int i=0;i<26;i++)
        if(id[son[p][i]]==1&&ml>len[son[p][i]])
            j=i,ml=len[son[p][i]];
    if(p==1&&j==26)printf("Impossible");
    if(j!=26)putchar('a'+j),W(son[p][j]);
}
void solve(int t)
{
    clear();n=getint();
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s);last=1;
        for(int j=0,l=strlen(s);j<l;j++)extend(s[j]-'a'),id[last]=i;
    }
    for(int i=0;i<=tot;i++)c[i]=0;
    for(int i=1;i<=tot;i++)c[mx[i]]++;
    for(int i=1;i<=tot;i++)c[i]+=c[i-1];
    for(int i=tot;i;i--)q[c[mx[i]]--]=i;
    for(int i=tot,u;i;i--)
    {
        u=q[i],id[u],len[u]=(id[u]==1);
        if(fa[u])id[fa[u]]=max(id[fa[u]],id[u]);
    }
    for(int i=tot;i;i--)
    {
        int u=q[i];
        for(int j=0;j<26;j++)if(id[son[u][j]]==1)
        {
            int v=son[u][j];
            if(id[u]!=1)id[u]=1,len[u]=len[v]+1;
            else len[u]=min(len[u],len[v]+1);
        }
    }
    printf("Case #%d: ",t);
    W(1),putchar('\n');
}
int main()
{
    //freopen("lx.in","r",stdin);
    for(int T=getint(),t=1;T;T--,t++)solve(t);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值