SPOJ1812 Longest Common Substring II 后缀自动机

题目链接点我点我:-)

题目描述
n(n<=10) 个字符串的最长公共子串的长度,每个字符串长度小于等于 105

思路
对于第一个串建后缀自动机,然后别的串在上面匹配,
匹配的过程和SPOJ1811差不多,但是要记下经过的每一个节点的当前匹配的最长长度。
然后,按照拓补序去更新pre的最长长度的值,拓补序就是step值的大小排序,这个应该很好证明
最后记录所有的串中的对于后缀自动机的每一个节点的匹配最短长度取最大值即可

感想
看了半天题解,还又T又WA,spoj卡时间!
开始并不晓得拓补序的搞法,T惨了!
觉得自己很理解这个算法了

代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

using namespace std;

#define Set(a, v) memset(a, v, sizeof(a))
#define For(i, a, b) for(int i = (a); i <= (int)(b); ++i)
#define N (10+5)
#define SN (200000+5)
#define INF 0x3f3f3f3f

struct node{
    int cnt, id;
    bool operator <(const node rhs)const{
        return cnt > rhs.cnt;
    }
}tp[SN];

int np, p, q, nq, len, Max, tle;

struct Suffix_Automaton{
    int root, last, Tot, ch[SN][26], pre[SN], step[SN], nl[SN], ml[SN];

    inline void init(){
        root = last = ++Tot;
        Set(ml, INF);
    }

    inline void Insert(char nc){
        nc = nc-'a';
        np = ++Tot, p = last;
        step[np] = step[last]+1; last = np;
        for(;p&&!ch[p][nc]; p = pre[p]) ch[p][nc] = np;

        if(!p) pre[np] = root;
        else{
            q = ch[p][nc];
            if(step[q]==step[p]+1){
                pre[np] = q; return;
            }

            nq = ++Tot;
            step[nq] = step[p]+1;
            pre[nq] = pre[q]; pre[np] = pre[q] = nq;
            For(i, 0, 25) ch[nq][i] = ch[q][i];
            for(;ch[p][nc]==q; p=pre[p]) ch[p][nc] = nq;
        }
    }

    inline void mktp(){
        For(i, 1, Tot){tp[i].id = i; tp[i].cnt = ml[i] = step[i];}
        sort(tp+1, tp+Tot+1);
    }

    inline void Find(char *s){
        For(i, 1, Tot) nl[i] = 0;
        len = 0, Max = 0, p = root;
        char nc;

        tle = strlen(s)-1;
        For(i, 0, tle){
            nc = s[i]-'a';

            if(ch[p][nc]) p = ch[p][nc], nl[p] = max(nl[p], ++len);
            else{
                for(; p&&!ch[p][nc]; p = pre[p]);
                if(!p) p = root, len = 0;
                else{
                    nl[ch[p][nc]] = max(nl[ch[p][nc]], len=step[p]+1);
                    p = ch[p][nc];
                }
            }
            Max = max(Max, len);
        }
        For(i, 1, Tot){
            p = tp[i].id;
            nl[pre[p]] = max(nl[pre[p]], min(step[pre[p]], nl[p]));
            ml[p] = min(ml[p], nl[p]);
        }
    }

    inline void print(){
        int ans = 0;
        For(i, 1, Tot) ans = max(ans, ml[i]);
        printf("%d\n", ans);
    }
}sat;

char r[SN];

int main(){
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif

    sat.init(); scanf("%s", r);
    tle = strlen(r)-1;
    For(i, 0, tle) sat.Insert(r[i]);
    sat.mktp();
    while(scanf("%s", r) != EOF) sat.Find(r);
    sat.print();

    return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值