[Spoj1812]LCS2

题意

n n 个串的LCS


题解

考虑怎么求两个串的 LCS L C S ,也就是 [Spoj1811] [ S p o j 1811 ]

先对其中一个串建立 SAM S A M ,令 x=1,len=0 x = 1 , l e n = 0 (我的 SAM S A M 1 1 开始)

然后对于第二个字符串s我们一位一位考虑

根据 SAM S A M 的性质

1. 1. 如果 x x si这个儿子,我们可以直接跳下去,然后 len++ l e n + +

2. 2. 否则让 x x 不断在parent树上跳,直到 x x si这个儿子为止,同时 len=Len(x)+1 l e n = L e n ( x ) + 1 ,然后 x x 再跳下去

3.如果 x x 跳出SAM了那就令 x=1,len=0 x = 1 , l e n = 0 重新开始

因为 Len(LCS)n L e n ( L C S ) ≤ n ,每次在 parent p a r e n t 树上跳长度就会变小,所以最多会跳 Len(LCS) L e n ( L C S ) 次,所以复杂度是 O(n) O ( n )

Hint: H i n t : 写的时候可以吧 1,2 1 , 2 种情况合并一下

那么多个串呢?

还是对第一个串建 SAM S A M ,然后一个一个串处理

在处理每一个串的时候记录一下在每个节点最大匹配长度

然后因为有很多串,所以要再记录一下上面的东西的最小值,才是对所有串匹配长度

然后注意到一个能点被匹配,那么他在 parent p a r e n t 树上的祖先都能被匹配到

所以对于每一个点,要对他子树的答案取 max m a x ,然后还有对自己的长度取 min m i n

不然的话这个点的最长匹配答案可能就会出错,然后影响全局的最小值,从而影响答案

试一下下面这组数据

dccccacdcbdbbbadbaa
cdcdbadaaabeddbddae
abdbecbddceaebacaca
debabcaccbbceccdcadd
#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=2.5e5+5,M=2*N;
typedef int arr[N];
struct SAM{
    int las,T,fa[M],len[M],ch[M][26];
    inline void init(){las=T=1;}
    inline void ins(int c){
        int p=las,np;fa[las=np=++T]=1,len[T]=len[p]+1;
        for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
        if(p){
            int q=ch[p][c],nq;
            if(len[p]+1==len[q])fa[np]=q;
            else{
                fa[nq=++T]=fa[q],len[T]=len[p]+1;memcpy(ch[nq],ch[q],4*26);
                for(fa[q]=fa[np]=nq;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
            }
        }
    }
    int c[M],b[M];
    inline void sort(){
        fp(i,1,T)++c[len[i]];
        fp(i,1,T)c[i]+=c[i-1];
        fp(i,1,T)b[c[len[i]]--]=i;
    }
    int mx[M],mi[M];
    inline void work(char*s){
        int l=0,x=1,v;
        fp(i,0,strlen(s)-1){
            v=s[i]-'a';while(x&&!ch[x][v])x=fa[x],l=len[x];
            if(x)++l,x=ch[x][v],cmax(mx[x],l);else x=1,l=0;
        }
        fd(i,T,1){
            int u=b[i],f=fa[u];
            cmax(mx[f],min(mx[u],len[f]));
            cmin(mi[u],mx[u]);mx[u]=0;
        }
    }
}p;
int ans;char s[N];
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    scanf("%s",s);p.init();
    fp(i,0,strlen(s)-1)p.ins(s[i]-'a');
    memset(p.mi,63,sizeof p.mi);p.sort();
    while(~scanf("%s",s))p.work(s);
    fp(i,1,p.T)cmax(ans,p.mi[i]);
    printf("%d",ans);
return 0;
}

最近发现数组版的 SAM S A M 比结构体版快好多,难道因为每个 node n o d e 里面还嵌套了数组么 qwq q w q

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值