题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2806
题意:
思路:首先将字典串插入SAM。之后就是一个DP。设给出的串为S。由于单调性,我们首先二分答案L。我们用d[i]表示从i向前最大长度的S子串使得该子串为在字典中,那么有:
用f[i]表示到S[i]能匹配到的最大值,那么有:
下面我们证明i-d[i]单调不减:如果有某个i,使得i-d[i]>i+1-d[i+1],由d[i]的意义知S[i-d[i]+1,i]和S[i+1-d[i+1]+1,i+1]是字典中某个串的子串。由于i-d[i]+1>i+1-d[i+1]+1,知S[i+1-d[i+1]+1,i]也是字典中某个串的子串且比S[i-d[i]+1,i]更长,这与d[i]的定义中的max违背。因此i-d[i]<=i+1-d[i+1]。这样的话决策点是单调的,可以用单调队列维护。因此上面的式子我们用g[j]=f[j]-j。
struct SAM
{
SAM *son[3],*pre;
int len;
};
SAM sam[N],*head,*last;
int cnt;
void initSam()
{
head=last=&sam[0];
cnt=1;
}
void insert(int x)
{
SAM *p=&sam[cnt++],*u=last;
p->len=last->len+1;
last=p;
for(;u&&!u->son[x];u=u->pre) u->son[x]=p;
if(!u) p->pre=head;
else if(u->son[x]->len==u->len+1) p->pre=u->son[x];
else
{
SAM *r=&sam[cnt++],*q=u->son[x];
*r=*q; r->len=u->len+1;
p->pre=q->pre=r;
for(;u&&u->son[x]==q;u=u->pre) u->son[x]=r;
}
}
int n,m,len,M;
char s[N];
int d[N];
void init()
{
SAM *p=head;
int i,cur=0,x;
FOR1(i,M)
{
x=s[i-1]-48;
if(p->son[x]) cur++,p=p->son[x];
else
{
while(p&&!p->son[x]) p=p->pre;
if(!p) p=head,cur=0;
else cur=p->len+1,p=p->son[x];
}
d[i]=cur;
}
}
int f[N],g[N],Q[N];
int OK(int L)
{
int head=1,tail=0,i,w;
FOR1(i,M)
{
w=i-L;
if(w>=0)
{
while(head<=tail&&g[Q[tail]]<g[w]) tail--;
Q[++tail]=w;
}
while(head<=tail&&Q[head]<i-d[i]) head++;
f[i]=f[i-1];
if(head<=tail&&f[i]<g[Q[head]]+i) f[i]=g[Q[head]]+i;
g[i]=f[i]-i;
}
return f[M]>=len;
}
int main()
{
initSam();
RD(n,m);
int i;
while(m--)
{
RD(s);
for(i=0;s[i];i++) insert(s[i]-48);
if(m) insert(2);
}
int low,high,mid,ans;
while(n--)
{
RD(s); M=strlen(s);
len=ceil(M*0.9);
init();
low=1,high=M;
while(low<=high)
{
mid=(low+high)/2;
if(OK(mid)) low=mid+1;
else high=mid-1;
}
if(low<=M&&OK(low)) PR(low);
else PR(high);
}
}