bzoj2806: [Ctsc2012]Cheat

被hhn d飞a 一直不想(gan)做这题

首先先把作文库插入SAM,两两间插个2(这里产生很多细节!空间要开到3,而且深度是要累计的)

对于每个作文,先在SAM跑一遍,求出match数组,该数组表示以当前为结束点往前最长能被自动机识别的长度

二分答案

考虑DP,f[i]表示到第i个位置,最多能够识别多少字符

那么明显f[i]=max(f[j]+i-j) 并且j>i-match[i]  因为f[j]已经搞好,对于j+1~i,都是能被匹配的

然后j<i-L,就是题意限制了。

这个DP是O(n^2)的啊,但是因为i-L单调可以用单调队列优化。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const double li=0.8999999999; 
char ss[110000];

struct SAM
{
    int w[3],parent,L;
}ch[1100000];int root,cnt,last;
void add(int i,int dep)
{
    int x=ss[i]-'0';
    int now=++cnt,p=last;
    ch[now].L=dep;
    
    while(p!=0&&ch[p].w[x]==0)ch[p].w[x]=now,p=ch[p].parent;
    if(p==0)ch[now].parent=root;
    else
    {
        int pre=ch[p].w[x];
        if(ch[pre].L==ch[p].L+1)ch[now].parent=pre;
        else
        {
            int npre=++cnt;
            ch[npre]=ch[pre];
            ch[npre].L=ch[p].L+1;
            
            ch[pre].parent=ch[now].parent=npre;
            while(p!=0&&ch[p].w[x]==pre)ch[p].w[x]=npre,p=ch[p].parent;
        }
    }
    last=now;
}

//---------------init-----------------------------

int match[110000];
void getmatch(int len)
{
    int now=root,sum=0;
    for(int i=1;i<=len;i++)
    {
        int x=ss[i]-'0';
        if(ch[now].w[x]!=0) sum++, now=ch[now].w[x];
        else
        {
            while(now!=0&&ch[now].w[x]==0)now=ch[now].parent;
            if(now==0) sum=0, now=root;
            else sum=ch[now].L+1, now=ch[now].w[x];
        }
        match[i]=sum;
    }
}

int f[110000],list[110000];
bool check(int mid,int len)
{
    int head=1,tail=1;
    f[0]=0;list[1]=0;
    for(int i=1;i<=len;i++)
    {
        int u=i-mid;f[i]=f[i-1];
        if(u<0)continue;
        
        while(head<=tail&&f[list[tail]]+i-list[tail]<=f[u]+i-u)tail--;
        list[++tail]=u;
        while(head<=tail&&list[head]<i-match[i])head++;
        if(head<=tail)f[i]=max(f[i],f[list[head]]+i-list[head]);
    }
    if( (double(f[len]))/(double(len)) >=li)return true;
    return false;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    root=cnt=last=1;
    int dep=0; 
    for(int i=1;i<=m;i++)
    {
        scanf("%s",ss+1);
        int len=strlen(ss+1);ss[++len]='2';
        for(int j=1;j<=len;j++)
        {
            dep++;
            add(j,dep);
        }
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ss+1);int len=strlen(ss+1); 
        getmatch(len);
        int l=1,r=len,ans=0;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(check(mid,len)==true)
            {
                l=mid+1;
                ans=mid; 
            }
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2018-04-24 10:33  AKCqhzdy  阅读(147)  评论(0编辑  收藏  举报