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

【[SDOI2008]Sandy的卡片】

时间:2019-01-01 21:11:50      阅读:129      评论:0      收藏:0      [点我收藏+]

标签:++   mat   简单   ring   二分   i+1   code   return   clu   

\(mhr\)的暴力干翻了

这道题做法还是非常好想的

先做一遍差分,在每个串的某尾插入一个特殊字符,再将所有的串拼接在一起

现在的问题就转化为找到一个最长的公共子串使得其出现了\(n\)次,但是在一个串内出现多次出现只算一次

先考虑一下没有第二个限制的做法

那就是最简单的\(SA\)+二分了,就是扫一遍\(height\)数组,之后根据\(height\)进行分组使得一个组内所有的\(height\)都大于等于当前二分出来的\(mid\),之后一个组内有多少个就代表这个子串出现了多少次

现在有了限制,就是不能来自于同一个串

于是我们给每一个\(sa_i\)打一个标记,标记好其来自哪一个数组,之后对于一个组内出现的同一个串内的我们只需要算一次就够了

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define LL long long
#define maxn (1000*100+1000+5)*2
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define pt putchar(1)
inline int read()
{
    char c=getchar();int x=0;
    while(c<‘0‘||c>‘9‘) c=getchar();
    while(c>=‘0‘&&c<=‘9‘) x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int a[maxn],f[maxn],c[maxn];
int tp[maxn],sa[maxn],rk[maxn],tax[maxn],het[maxn],b[maxn];
int n,m,K,r,t;
inline void qsort()
{
    for(re int i=0;i<=K;i++) tax[i]=0;
    for(re int i=1;i<=n;i++) tax[rk[i]]++;
    for(re int i=1;i<=K;i++) tax[i]+=tax[i-1];
    for(re int i=n;i;--i) sa[tax[rk[tp[i]]]--]=tp[i];
}
inline int check(int x)
{
    int num=0,now=1;
    if(f[sa[1]]) num++,b[f[sa[1]]]++;
    for(re int i=2;i<=n;i++)
    {
        if(het[i]<x)
        {
            if(num>=m) return 1;
            for(re int j=now;j<i;j++) b[f[sa[j]]]=0;
            num=0,now=i;
            if(f[sa[i]]) num++,b[f[sa[i]]]++;
            continue;
        }
        if(!b[f[sa[i]]]&&f[sa[i]]) b[f[sa[i]]]++,num++;
    }
    return num>=m;
}
int main()
{
    m=read();
    for(re int i=1;i<=m;i++)
    {
        t=read();r=max(t,r);
        for(re int j=1;j<=t;j++) a[++n]=read(),f[n]=i;
        a[++n]=i+101,f[n]=0;
    }
    for(re int i=1;i<=n;i++) c[i]=a[i]-a[i-1];
    for(re int i=1;i<=n;i++) c[i]+=2001,K=max(c[i],K);
    c[1]=0;
    for(re int i=1;i<=n;i++) tp[i]=i,rk[i]=c[i];
    qsort();
    for(re int w=1,p=0;p<n;K=p,w<<=1)
    {
        p=0;
        for(re int i=1;i<=w;i++) tp[++p]=n-w+i;
        for(re int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
        qsort();
        for(re int i=1;i<=n;i++) std::swap(rk[i],tp[i]);
        rk[sa[1]]=p=1;
        for(re int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
    }
    int k=0;
    for(re int i=1;i<=n;i++)
    {
        if(k) --k;
        int j=sa[rk[i]-1];
        while(c[i+k]==c[j+k]) ++k;
        het[rk[i]]=k;
    }
    int l=1,ans=0;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid)) l=mid+1,ans=mid;
            else r=mid-1;
    }
    printf("%d\n",ans+1);
    return 0;
}

【[SDOI2008]Sandy的卡片】

标签:++   mat   简单   ring   二分   i+1   code   return   clu   

原文地址:https://www.cnblogs.com/asuldb/p/10205635.html

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