码迷,mamicode.com
首页 > 其他好文 > 详细

P2463 [SDOI2008]Sandy的卡片

时间:2019-12-17 22:11:56      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:sdoi   并且   memset   int   ++   sandy   bool   break   eof   

题意

显然加上一个数相等就是差分数组相等,于是问题变为求几个串的最长公共子串。

这里我学习了如何用SA求LCS。

首先问题要转化成求一些后缀的最长公共前缀,要求这些后缀分属不同的串。

于是二分答案,于是问题就变成求一段连续的\(height\)数组,它们都\(\geqslant mid\),并且至少分属\(n\)个串,显然可以\(O(n)\)扫一遍得出。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int n,num,K;
int a[maxn],b[maxn],pos[maxn];
int sa[maxn],rk[maxn],oldrk[maxn],id[maxn],tmpid[maxn],cnt[maxn],height[maxn];
bool vis[maxn];
inline bool cmp(int x,int y,int k){return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];}
inline void sa_build()
{
    num=3000;
    for(int i=1;i<=n;i++)cnt[rk[i]=a[i]]++;
    for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
    for(int i=n;i;i--)sa[cnt[rk[i]]--]=i;
    for(int t=1;t<=n;t<<=1)
    {
        int tot=0;
        for(int i=n-t+1;i<=n;i++)id[++tot]=i;
        for(int i=1;i<=n;i++)if(sa[i]>t)id[++tot]=sa[i]-t;
        tot=0;
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)cnt[tmpid[i]=rk[id[i]]]++;
        for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
        for(int i=n;i;i--)sa[cnt[tmpid[i]]--]=id[i];
        memcpy(oldrk,rk,sizeof(rk));
        for(int i=1;i<=n;i++)rk[sa[i]]=cmp(sa[i-1],sa[i],t)?tot:++tot;
        num=tot;
        if(num>=n)break;
    }
    for(int i=1,j=0;i<=n;i++)
    {
        if(j)j--;
        while(a[i+j]==a[sa[rk[i]-1]+j])j++;
        height[rk[i]]=j;
    }
}
inline bool work(int l,int r)
{
    if(r-l+1<K)return 0;
    int tot=0;
    memset(vis,0,sizeof(vis));
    for(int i=l;i<=r;i++)
        if(!vis[pos[sa[i]]])
            tot++,vis[pos[sa[i]]]=1;
    return tot==K;
}
inline bool check(int mid)
{
    int l=1,r=1;
    while(l<=n)
    {
        while(height[r+1]>=mid&&r<n)r++;
        if(work(l,r))return 1;
        l=r+1;r=l;
    }
    return 0;
}
const int delta=1900; 
int main()
{
    scanf("%d",&K);
    for(int i=1;i<=K;i++)
    {
        int k;scanf("%d",&k);
        for(int j=0;j<k;j++)scanf("%d",&b[j]);
        for(int j=1;j<k;j++)a[++n]=b[j]-b[j-1],pos[n]=i;
        a[++n]=i+delta;pos[n]=i;
    }
    sa_build();
    int l=0,r=n,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d",ans+1);//+1!!!
    return 0;
}

P2463 [SDOI2008]Sandy的卡片

标签:sdoi   并且   memset   int   ++   sandy   bool   break   eof   

原文地址:https://www.cnblogs.com/nofind/p/12056985.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!