[SPOJ1812]-LCS2-后缀自动机
2018-01-17 17:59
441 查看
说在前面
并没有什么想说的,但是要保持格式=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 ) ; }
相关文章推荐
- SPOJ 1812 Longest Common Substring II(后缀自动机)(LCS2)
- SPOJ 1812 LCS2 后缀自动机
- spoj 1812 LCS2 - Longest Common Substring II (后缀自动机)
- [SPOJ1812]LCS2 - Longest Common Substring II(后缀自动机)
- [SPOJ]1812 LCS2 后缀自动机
- SPOJ 1812 LCS2 [后缀自动机 DP]
- spoj 1812 LCS2 (后缀自动机)
- SPOJ 题目1812 LCS2 - Longest Common Substring II(后缀自动机求多个串的最长公共子串)
- SPOJ 1812 LCS2 - Longest Common Substring II (后缀自动机)
- [SPOJ1812]LCS2 - Longest Common Substring II-后缀自动机
- 【SPOJ】1812. Longest Common Substring II(后缀自动机)
- SPOJ LCS(Longest Common Substring-后缀自动机-结点的Parent包含关系)
- SPOJ - LCS2 Longest Common Substring II 后缀自动机
- SPOJ 1812 Longest Common Substring II(后缀自动机)
- 【后缀自动机】 SPOJ LCS
- spoj LCS 后缀自动机
- 【后缀自动机】[SPOJLCS]Longest Common Substring
- spoj 1811 LCS 后缀自动机
- SPOJ LCS 最长公共子串 后缀自动机&后缀树(Ukkonen)
- SPOJ 1811 LCS 后缀自动机