[bzoj2806][SAM][DP]Cheat

123 篇文章 1 订阅
8 篇文章 0 订阅

Description

这里写图片描述

Input

第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库 的行数 接下来M行的01串,表示标准作文库 接下来N行的01串,表示N篇作文

Output

N行,每行一个整数,表示这篇作文的Lo 值。

Sample Input

1 2

10110

000001110

1011001100

Sample Output

4

HINT

输入文件不超过1100000字节

注意:题目有改动,可识别的长度不小于90%即可,而不是大于90%

题解

首先第一眼就是广义SAM
然后机房有人说不用建广义中间加个分隔符就可以了
那我就中间加个分隔符吧2333
然后一开始打了个n^2的暴力dp妥妥tle
怎么优化成n呢
原谅我一开始的状态没办法优化
设f[i]表示1~i最多能识别多少字符
那么f[i]=min(f[j]+i-j)(i-mt[i]<=f[j]<=i-L),mt[i]表示以i结尾往前最多能识别多少位,L我们可以二分嘛
mt[i]同样也可以在SAM上预处理出来
最后单调队列优化dp即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct SAM
{
    int parent,dep,son[3];
}tr[4100000];int cnt,root,last;
char ch[1110000];
void add(int x,int op)
{
    int np=++cnt;
    tr[np].dep=op;
    int p=last;
    while(p && tr[p].son[x]==0)tr[p].son[x]=np,p=tr[p].parent;
    if(p==0)tr[np].parent=root;
    else
    {
        int q=tr[p].son[x];
        if(tr[q].dep==tr[p].dep+1)tr[np].parent=q;
        else
        {
            int nq=++cnt;
            tr[nq]=tr[q];tr[nq].dep=tr[p].dep+1;
            tr[q].parent=tr[np].parent=nq;
            while(p && tr[p].son[x]==q)tr[p].son[x]=nq,p=tr[p].parent;
        }
    }
    last=np;
}
int n,m,vis[1110000],tt;
int f[1110000];//前i个 最多多少匹配 
int list[1110000],head,tail;
int mt[1110000];
/*void gtans(int now,int L)
{
    f[now]=now;
    int p=root;bool bk=false;
    for(int i=now;i>=1;i--)
    {
        if(tr[p].son[ch[i]-'0']==0)bk=true;
        if(bk==false)p=tr[p].son[ch[i]-'0'];
        if(now-i+1>=L && bk==false)f[now]=min(f[now],f[i-1]);
        f[now]=min(f[now],f[i-1]+now-i+1);
    }
}*/
bool check(int p,int len)
{
    f[0]=0;
    list[1]=0;head=tail=1;
    for(int i=1;i<=len;i++)
    {
        f[i]=f[i-1];int pos=i-p;if(pos<0)continue;
        while(head<=tail && f[pos]+i-pos>f[list[tail]]+i-list[tail])tail--;list[++tail]=pos;
        while(head<=tail && list[head]<i-mt[i])head++;
        if(head<=tail)f[i]=max(f[i],f[list[head]]+i-list[head]);
    }
    if((double(f[len])/double(len)>=0.899999999))return true;
    return false;
}
int main()
{
  //freopen("cheat10.in","r",stdin);
 //freopen("cheat.out","w",stdout);
    scanf("%d%d",&n,&m);
    root=last=++cnt;int lg=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%s",ch+1);int len=strlen(ch+1);
        for(int j=1;j<=len;j++)add(ch[j]-'0',++lg);
        add(2,++lg);
    }
    tt=0;
    while(n--)
    {
        scanf("%s",ch+1);int len=strlen(ch+1);
        int p=root,cnt=0;
        for(int i=1;i<=len;i++)
        {
            int x=ch[i]-'0';
            if(tr[p].son[x]!=0)p=tr[p].son[x],cnt++;
            else
            {
                while(p && tr[p].son[x]==0)p=tr[p].parent;
                if(p==0)cnt=0,p=root;
                else cnt=tr[p].dep+1,p=tr[p].son[x];
            }
            mt[i]=cnt;
        }
        int l=0,r=len,ans=0;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(mid==0)break;
            if(check(mid,len))ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值