P4022 [CTSC2012]熟悉的文章(广义SAM+决策单调性)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4022 [CTSC2012]熟悉的文章(广义SAM+决策单调性)相关的知识,希望对你有一定的参考价值。
显然 L 0 L_0 L0有单调性,从而转化为判定性问题
由于需要超过原串长度的 90 % 90\\% 90%,不妨定义 f [ i ] f[i] f[i]表示前 i i i个字符形成的最大熟悉字符
使用后缀自动机可以预处理一个 s u f i suf_i sufi表示最长可以使 [ i − s u f i + 1 , i ] [i-suf_i+1,i] [i−sufi+1,i]能匹配上原串
先继承上一次的状态 f [ i ] = f [ i − 1 ] f[i]=f[i-1] f[i]=f[i−1]
若 s u f i > = L 0 suf_i>=L_0 sufi>=L0,说明这个后缀可以形成熟悉子串
令 l = i − s u f i , r = i − L 0 l=i-suf_i,r=i-L_0 l=i−sufi,r=i−L0
那么有 f [ i ] = max j = l r { f [ j ] + i − j } f[i]=\\max_{j=l}^{r}\\{f[j]+i-j\\} f[i]=maxj=lr{f[j]+i−j}
这部分维护一个 f [ j ] − j f[j]-j f[j]−j的线段树区间 m a x max max就可以解决
时间复杂度 O ( n l o g 2 ) O(nlog^2) O(nlog2)
复杂度还不够优秀,进一步观察到每次只需要从 [ i − s u f i , i − L 0 ] [i-suf_i,i-L_0] [i−sufi,i−L0]转移而来
发现具有决策单调性,因为 i − s u f i i-suf_i i−sufi单调递增,于是可以套一个单调队列求区间最值
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2.4e6+10;
const int inf = 1e9;
int n,m,len;
char a[maxn];
int zi[maxn][2],l[maxn],fa[maxn],id = 1,las = 1;
void insert(int c)
{
int p = las, np = ++id; las = id;
l[np] = l[p]+1;
for( ; p && zi[p][c]==0 ;p=fa[p] ) zi[p][c] = np;
if( !p ) fa[np] = 1;
else
{
int q = zi[p][c];
if( l[q]==l[p]+1 ) fa[np] = q;
else
{
int nq = ++id;
fa[nq] = fa[q], l[nq] = l[p]+1;
memcpy( zi[nq],zi[q], sizeof zi[q] );
fa[np] = fa[q] = nq;
for( ; p&&zi[p][c]==q; p=fa[p] ) zi[p][c] = nq;
}
}
}
int suf[maxn],f[maxn],L;
int tail,head,q[maxn];
int isok(int L0)
{
head = 1, tail = 0; int z = 0;
for(int i=1;i<=len;i++)
{
f[i] = f[i-1];
if( suf[i]<L0 ) continue;
while( z<=i-L0 )
{
while( tail>=head && f[q[tail]]-q[tail]<=f[z]-z ) tail--;
q[++tail] = z; z++;
}
while( q[head]<i-suf[i] ) head++;
f[i] = max( f[i], f[q[head]]-q[head]+i );
if( f[i]*10>=len*9 ) return f[i];
}
return f[len];
}
int main()
{
scanf("%d%d",&n,&m );
for(int i=1;i<=m;i++)
{
scanf("%s",a+1 );
int le = strlen( a+1 );
for(int j=1;j<=le;j++) insert( a[j]-'0' );
las = 1;
}
while( n-- )
{
scanf("%s",a+1 ); len = strlen( a+1 );
int L = 0, p = 1;
for(int i=1;i<=len;i++)
{
int c = a[i]-'0';
if( zi[p][c] ) p = zi[p][c], L++;
else
{
while( p&&zi[p][c]==0 ) p = fa[p];
if( !p ) p = 1, L = 0;
else L = l[p]+1, p = zi[p][c];
}
suf[i] = L;
}
int l = 1, r = len, ans = 0;
while( r>=l )
{
int mid = l+r>>1;
if( isok(mid)*10>=len*9 ) l = mid+1, ans = mid;
else r = mid-1;
}
printf("%d\\n",ans );
}
}
以上是关于P4022 [CTSC2012]熟悉的文章(广义SAM+决策单调性)的主要内容,如果未能解决你的问题,请参考以下文章
[BZOJ2806][CTSC2012]熟悉的文章(Cheat)