SPOJ 1812 LCS2

后缀自动机

对于其中一个串建出SAM,把别的串S放在上面跑。对于每一个经过的节点记录串S能贡献给这个节点的max,再对每一个跑过的串在这个节点上取min,最终节点里min贡献的max就是答案。显然一个串能贡献一个节点,就能贡献这个节点的所有祖先,即fail树上到祖先的链都要更新。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 200005
#define A 28
#define cmax(u,v) ((u)<(v)?(u)=(v):0)
#define cmin(u,v) ((u)>(v)?(u)=(v):0)
using namespace std;
namespace runzhe2000
{
    int sum[N];
    char s[N];
    struct SAM
    {
        SAM *next[A], *fail;
        int len, mi, mx;
    }mem[N], *tot, *null, *root, *last, *q[N];

    SAM *newSAM(int len)
    {
        SAM *p = ++tot;
        *p = *null; 
        p->len = p->mi = len;
        p->mx = 0;
        return p;
    }

    void init()
    {
        null = tot = mem;
        for(int i = 0; i < A; i++) null->next[i] = null;
        null->fail = null;
        null->len = null->mi = null->mx = 0;
        root = last = newSAM(0);
    }

    void extend(int v)
    {
        SAM *p = last, *np = newSAM(p->len + 1); last = np;
        for(; p->next[v] == null && p != null; p = p->fail) p->next[v] = np;
        if(p==null) np->fail = root;
        else
        {
            SAM *q = p->next[v];
            if(q->len == p->len+1) np->fail = q;
            else
            {
                SAM *nq = newSAM(p->len+1);
                memcpy(nq->next, q->next, sizeof(nq->next));
                nq->fail = q->fail;
                q->fail = np->fail = nq;
                for(; p->next[v] == q && p != null; p = p->fail) p->next[v] = nq;
            }
        }
    }

    void main()
    {
        init(); scanf("%s",s);
        for(int i = 0, ii = strlen(s); i < ii; i++) extend(s[i] - 'a');

        int totmem = tot - mem;
        for(SAM *p = tot; p != mem; p--) sum[p->len]++;
        for(int i = 1, ii = strlen(s); i <= ii; i++) sum[i] += sum[i-1];
        for(SAM *p = tot; p != mem; p--) q[sum[p->len]--] = p;

        for(; scanf("%s",s) != EOF; )
        {
            SAM *p = root; int len = 0;
            for(int i = 0; s[i]; i++)
            {
                int v = s[i] - 'a';
                if(p->next[v] != null)
                {
                    len++; 
                    p = p->next[v];
                    cmax(p->mx, len);
                }
                else
                {
                    for(; p->next[v] == null && p != null; p = p->fail);
                    if(p->next[v] != null) 
                    {
                        len = p->len+1;
                        p = p->next[v];
                        cmax(p->mx, len);
                    }
                    else p = root;
                }
            }
            for(int i = totmem; i != 1; i--)
            {
                SAM *p = q[i];
                cmin(p->mi, p->mx);
                cmax(p->fail->mx, min(p->fail->len,p->mx));
                p->mx = 0;
            }
        }
        int ans = 0;
        for(int i = totmem; i; i--) cmax(ans, mem[i].mi);
        printf("%d\n",ans);
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值