[Luogu2463][SDOI2008]Sandy的卡片

Posted 租酥雨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Luogu2463][SDOI2008]Sandy的卡片相关的知识,希望对你有一定的参考价值。

BZOJ权限题qwq
Luogu

sol

“两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串”
其实就是差分一波以后完全相同
所以对输入的数据进行差分,同时记一下每一个位置是属于哪个串的。
记得在串与串中间加入一个没有出现的字符。
求出SA后二分答案\(mid\),问题变成:\(Height\)数组里是否存在一个大于等于\(mid\)的连续段使每个串都在里面出现过。
开桶记录。记一个\(id\)避免每次对桶的清空。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define ll long long
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e6+1e4+5;
int n,tot,a[N],b[N],t[N],x[N],y[N],SA[N],Rank[N],Height[N],id;
bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}
void getSA()
{
    int m=1e6+1e4;
    for (int i=1;i<=n;++i) ++t[x[i]=a[i]];
    for (int i=1;i<=m;++i) t[i]+=t[i-1];
    for (int i=n;i>=1;--i) SA[t[x[i]]--]=i;
    for (int k=1;k<=n;k<<=1)
    {
        int p=0;
        for (int i=0;i<=m;++i) y[i]=0;
        for (int i=n-k+1;i<=n;++i) y[++p]=i;
        for (int i=1;i<=n;++i) if (SA[i]>k) y[++p]=SA[i]-k;
        for (int i=0;i<=m;++i) t[i]=0;
        for (int i=1;i<=n;++i) ++t[x[y[i]]];
        for (int i=1;i<=m;++i) t[i]+=t[i-1];
        for (int i=n;i>=1;--i) SA[t[x[y[i]]]--]=y[i];
        swap(x,y);
        x[SA[1]]=p=1;
        for (int i=2;i<=n;++i) x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
        if (p>=n) break;
        m=p;
    }
    for (int i=1;i<=n;++i) Rank[SA[i]]=i;
    for (int i=1,j=0;i<=n;++i)
    {
        if (j) --j;
        while (a[i+j]==a[SA[Rank[i]-1]+j]) ++j;
        Height[Rank[i]]=j;
    }
}
bool check(int mid)
{
    ++id;int cnt=0;
    for (int i=1;i<=n;++i)
        if (Height[i]>=mid)
        {
            if (t[b[SA[i-1]]]!=id) t[b[SA[i-1]]]=id,++cnt;
            if (t[b[SA[i]]]!=id) t[b[SA[i]]]=id,++cnt;
            if (cnt==tot) return true;
        }
        else cnt=0,++id;
    return false;
}
int main()
{
    tot=gi();
    for (int i=1;i<=tot;++i)
    {
        for (int j=1,len=gi();j<=len;++j)
            t[j]=gi(),a[++n]=t[j]-t[j-1]+5e5,b[n]=i;
        a[++n]=1e6+i;
    }
    memset(t,0,sizeof(t));
    getSA();
    memset(t,0,sizeof(t));
    int l=0,r=n;
    while (l<r)
    {
        int mid=l+r+1>>1;
        if (check(mid)) l=mid;
        else r=mid-1;
    }
    printf("%d\n",l+1);
    return 0;
}

以上是关于[Luogu2463][SDOI2008]Sandy的卡片的主要内容,如果未能解决你的问题,请参考以下文章

[SDOI2008][luogu2463] Sandy的卡片 [kmp]

luogu 2463 [SDOI2008]Sandy的卡片 kmp || 后缀数组 n个串的最长公共子串

BZOJ4698 & 洛谷2463:[SDOI2008]Sandy的卡片——题解

bzoj4698 / P2463 [SDOI2008]Sandy的卡片

P2463 [SDOI2008]Sandy的卡片

luogu_2158 [SDOI2008]仪仗队