我是超链接
我们先把标准串建出一个广义后缀自动机
二分一个L,用dp判断可行性,dp?!
首先我们要用后缀自动机预处理出l[i],表示第i位一定选,可以匹配上的最长长度,即向前延伸最远可以和标准串匹配的长度
怎么用dp判可行啊?f[i]表示前i位能称为【熟悉】的最大长度,那么最后用f[n]和len比一比看看到不到90%就好,f[i]怎么求呢?
不难写出转移方程f[i]=max{f[i-1],f[j]+i-j} j∈[i−l[i],i−L] j ∈ [ i − l [ i ] , i − L ]
这个范围是需要我们注意的,i-l[i]是因为过了这个界就不能匹配了,也就是不能用i-j来表示长度了;i-L是被称为【熟悉】的限制
O(n2) O ( n 2 ) ?不行啊我们还是优化下看看能不能快一点
这个f[i]=f[i-1]可以赋为初值,当然如果i < L的话就不可能被称为【熟悉】了直接过
l[i]+1>=l[i+1] l [ i ] + 1 >= l [ i + 1 ]
i+1−l[i]−1<=i+1−l[i+1] i + 1 − l [ i ] − 1 <= i + 1 − l [ i + 1 ]
i−l[i]<=i+1−l[i+1] i − l [ i ] <= i + 1 − l [ i + 1 ]
所以i-l[i]是单调不降的,我们可以用单调队列维护区间f[i]-i的最值。
我们之前已经说过了,i结点的转移区间只有 [i−l[i],i−L]
每当i在向后移动的时候,唯一可能产生的新的转移点就是(i−L)
那我们就在队尾插入这个转移点:i−L
记住要先加入一个新点然后再处理head,why?因为i-l[i]不一定比i-L小,把head放在后面又处理掉一批不合法的情况。
信心满满交了上去,华丽丽的M了。。。。看到只有0/1之后眼泪掉下来
#include
#include
#include
using namespace std;
const int N=2200005;
int np,p,nq,q,last,cnt,ch[N][2],fa[N],step[N],l[N],len,f[N],que[N];
char st[N];
void insert(int c)
{
p=last; np=last=++cnt;
step[np]=step[p]+1;
while (p && !ch[p][c]) ch[p][c]=np,p=fa[p];
if (!p) {fa[np]=1;return;}
q=ch[p][c];
if (step[q]==step[p]+1){fa[np]=q;return;}
nq=++cnt; step[nq]=step[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
while (ch[p][c]==q) ch[p][c]=nq,p=fa[p];
}
void init()
{
p=1;int tmp=0;
for (int i=1;i<=len;i++)
{
int c=st[i]-'0';
if (ch[p][c]) p=ch[p][c],tmp++;
else
{
while (p && !ch[p][c]) p=fa[p];
if (!p) p=1,tmp=0;
else tmp=step[p]+1,p=ch[p][c];
}
l[i]=tmp;
}
}
bool check(int L)
{
if (L==0) return 1;
f[0]=0;
int head=1,tail=0;
for (int i=1;i<=len;i++)
{
f[i]=f[i-1];
if (icontinue;
while (head<=tail && f[que[tail]]-que[tail]-L<=f[i-L]-i) tail--;
que[++tail]=i-L;
while (head<=tail && que[head]if (head<=tail) f[i]=max(f[i],f[que[head]]+i-que[head]);
}
return f[len]*10>=len*9;
}
void solve()
{
init();
int l=0,r=len,ans;
while (l<=r)
{
int mid=(l+r)>>1;
if (check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
cnt=1;
for (int i=1;i<=m;i++)
{
scanf("%s",st+1);
int l=strlen(st+1);last=1;
for (int j=1;j<=l;j++) insert(st[j]-'0');
}
for (int i=1;i<=n;i++)
{
scanf("%s",st+1);
len=strlen(st+1);
solve();
}
}