[SPOJ1812]-LCS2-后缀自动机

说在前面

并没有什么想说的,但是要保持格式=w=


题目

SPOJ - LCS2传送门

题目大意

给出N个字符串(N不超过10,每个串长不超过100000),求出这N个字符串的最长公共子串
(Tip:子串是连续的,子序列是不连续的)

输入输出格式

输入格式:
一共N行,每行包含一个字符串

输出格式:
输出一行一个整数,表示最长公共子串


解法

这是一道后缀自动姬的基础应用题=w=
首先拿一个串来建立后缀自动姬,然后用其它的串在这个自动姬上匹配。
匹配的时候维护一个cnt表示当前匹配的长度。如果能够匹配就一直走,并且cnt加一;不能走就跳parent边,直到跳到一个有当前字符转移边的节点,然后把cnt设为当前节点的max_length,继续匹配。匹配完之后,自底向上更新每个节点的「节点当前最大匹配长度」(注意匹配长度不能超过max_length),然后用「节点当前最大匹配长度」去更新「节点全局匹配长度」。最后在这个 全局匹配长度 里取个max就是答案

关于跳parent的正确性,因为right集合之间是互相包含的,所以如果能走到当前节点,就表示至少min_len的长度被匹配上了,而parent的max_len是当前节点的min_len-1,所以parent也一定能匹配,而且可以匹配完。
当前串最大匹配长度,只是当前这一个串在后缀自动姬上跑出来的匹配长度,而要求的是所有串的公共子串,因此就定义了「节点当前最大匹配长度」和「节点全局匹配长度」这两个数组


下面是自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , id_c , ans ;
int Max[200005] , Min[200005] ;
char ss[100005] ;
struct Node{
    int len , id ;
    Node *ch[26] , *par ;
}w[200005] , *tw = w , *root , *last ;

void newNode( Node *&nd , int len ){
    nd = ++tw ;
    nd->id = ++id_c ;
    nd->len = len ;
}

void Insert( char cc ){
    Node *nd , *tmp = last ;
    newNode( nd , tmp->len + 1 ) ;
    short id = cc - 'a' ;

    for( ; tmp && !tmp->ch[id] ; tmp = tmp->par )
        tmp->ch[id] = nd ;
    if( !tmp ) nd->par = root ;
    else{
        Node *B = tmp->ch[id] ;
        if( tmp->len + 1 == B->len ) nd->par = B ;
        else{
            Node *nB ; newNode( nB , tmp->len + 1 ) ;
            memcpy( nB->ch , B->ch , sizeof( nB->ch ) ) ;
            nB->par = B->par ;
            B->par = nd->par = nB ;
            while( tmp && tmp->ch[id] == B ){
                tmp->ch[id] = nB ;
                tmp = tmp->par ;
            }
        }
    }
    last = nd ;
}

int tp[200005] , sa[200005] ;
void Rsort(){
    for( int i = 1 ; i <= id_c ; i ++ ) tp[ w[i].len ] ++ ;
    for( int i = 1 ; i <= N ; i ++ ) tp[i] += tp[i-1] ;
    for( int i = id_c ; i ; i -- ) sa[ tp[w[i].len] -- ] = i ;
}

void RUN(){
    Node *nd = root ;
    int len = 0 ;
    memset( Max , 0 , sizeof( Max ) ) ;
    for( int i = 1 ; i <= N ; i ++ ){
        int id = ss[i] - 'a' ;
        if( nd->ch[id] )
            len ++ , nd = nd->ch[id] ;
        else{
            while( nd && !nd->ch[id] ) nd = nd->par ;
            if( !nd ) len = 0 , nd = root ;
            else      len = nd->len + 1 , nd = nd->ch[id] ;
        }
        Max[ nd->id ] = max( Max[ nd->id ] , len ) ;
    //  printf( "Max[%d] is %d\n" , nd->id , Max[ nd->id ] ) ;
    }
    for( int i = id_c ; i > 1 ; i -- ){
        Node *nd = ( w + sa[i] ) ;
        if( Max[ nd->id ] ) Max[ nd->par->id ] = nd->par->len ;
    }
    for( int i = 2 ; i <= id_c ; i ++ )
        Min[i] = min( Min[i] , Max[i] ) ;
}

int main(){
    newNode( root , 0 ) ; last = root ;
    scanf( "%s" , ss + 1 ) ; N = strlen( ss + 1 ) ;
    for( int i = 1 ; i <= N ; i ++ ) Insert( ss[i] ) ;
    Rsort() ;

    memset( Min , 0x3f , sizeof( Min ) ) ;
    while( scanf( "%s" , ss + 1 ) != EOF ){
        N = strlen( ss + 1 ) ;
        RUN() ;
    }
    for( int i = 2 ; i <= id_c ; i ++ )
        ans = max( Min[i] , ans ) ;
    printf( "%d" , ans == Min[0] ? 0 : ans ) ;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值