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;
}