题意是求两个字符串的lcs,两个串都只包含小写字母。

本题既可以用后缀自动机,又可以用后缀数组。

对于后缀自动机,就是一道模板题,直接对于一个字符串建后缀自动机再用另一个串查询就行。

对于后缀数组,其实也是一道模板题,但不是后缀数组的模板,而是用后缀数组求同一字符串的两个后缀的最长公共前缀的模板。

用h(x)表示排序前在第x位的后缀s1与排序后排在s1前一位的s2(记为prev(x))的最长公共前缀。

这样就会发现这些性质:

1.想要求出排序前位于k1,k2的后缀的最长公共前缀,只需要(用后缀数组)找出它们在排序后的位置,再求区间最值就可以。

2.对于排序前位于k的后缀s,它和所有排序后在它前面后缀的最长公共前缀不会超过h(k)。

根据性质2,会发现可以将两个用一个奇怪的字符连成一个大串,再对这个大串求后缀数组。这样,对于所有满足x与prev(x)在奇怪的字符异侧的h(x)求最大值就可以了。

那么问题又来了,如何快速求h(x)?

直接比较显然是不行的。经过一番并不对劲的考虑,发现h(x)>=h(x-1)-1。这是因为h(x-1)表示第x-1位的后缀和第prev(x-1)位的后缀的最长公共前缀,而第x位的后缀相当于是将第x-1位后缀最前面的字符去掉后形成的。那么,将第prev(x-1)位的后缀最前面的字符去掉,得到的字符串也是后缀之一(可能为空),而且它与第x位的后缀的最长公共前缀是h(x-1)-1。也就是说,第x位的后缀在排序后前面存在一个后缀与它的最长公共前缀是h(x-1)-1。根据性质2,就可以得出h(x)>=h(x-1)-1了。这样就能大大地减少了复杂度。

感觉这题还是用后缀自动机更方便。

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#define maxn 500010
using namespace std;
inline int read()
{
    int xx=0,ff=1;
    char ch=getchar();
    while(isdigit(ch)==0&&ch!='-')ch=getchar();
    if(ch=='-')ff=-1,ch=getchar();
    while(isdigit(ch))xx=xx*10+ch-'0',ch=getchar();
    return xx*ff;
}
void write(int x)
{
    int ff=0;char ch[15];
    if(x<0)
    {
        x=-x;
        putchar('-');
    }
    while(x)ch[++ff]=(x%10)+'0',x/=10;
    if(ff==0)putchar('0');
    while(ff)putchar(ch[ff--]);
    putchar(' ');
}
int sa[maxn],ord[maxn],x[maxn],n,m=130;
int y[maxn],c[maxn],h[maxn];
char s[maxn];
void get_sa() 
{
    memset(x,0,sizeof(x));
    memset(y,0,sizeof(y));
    memset(c,0,sizeof(c));
    for (int i=0;i<n;i++) c[x[i]=s[i]]++;
    for (int i=1;i<m;i++) c[i]+=c[i-1];
    for (int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for (int k=1;k<=n;k<<=1) 
    {
        int p = 0;
        for (int i=n-k;i<n;i++) y[p++] = i;
        for (int i=0;i<n;i++) if (sa[i] >= k) y[p++] = sa[i] - k;
        for (int i=0;i<m;i++) c[i] = 0;
        for (int i=0;i<n;i++) c[x[y[i]]]++;
        for (int i=1;i<m;i++) c[i] += c[i-1];
        for (int i=n-1;i>=0;i--) sa[--c[x[y[i]]]] = y[i];
        swap(x,y);
        p = 1;
        x[sa[0]] = 0;
        for (int i=1;i<n;i++)
            x[sa[i]]= (y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p-1:p++;
        if (p>=n) break;
        m=p;
    }
}
void getheight()
{
    int i,j,k=0;
    memset(h,0,sizeof(h));
    for(i=0;i<n;i++) ord[sa[i]]=i;
    for(i=0;i<n;i++)
    {
        if(k) k--;
        if(ord[i]==0)continue;
        j=sa[ord[i]-1];
        while(s[i+k]==s[j+k]) k++;
        h[i]=k;
    }
}
int main()
{
    char s2[maxn];
    scanf("%s",s);
    int split=strlen(s);
    scanf("%s",s2);
    strcat(s,"A"); 
    strcat(s,s2);
    n=strlen(s);
    get_sa();
    getheight();
    int maxx=0;
    for(int i=1;i<n;i++)
    { 
        if((sa[i]>split&&sa[i-1]<split)||(sa[i-1]>split&&sa[i]<split))
        {
            maxx=max(maxx,h[sa[i]]);
        }
    }
    write(maxx);
    return 0;
}
SA

相关文章:

  • 2021-11-18
  • 2021-06-03
  • 2021-12-26
  • 2022-02-08
  • 2021-08-02
  • 2022-02-17
  • 2021-10-11
  • 2022-01-22
猜你喜欢
  • 2021-07-13
  • 2022-03-04
  • 2021-07-28
  • 2021-11-28
  • 2021-12-15
  • 2021-10-29
相关资源
相似解决方案