P4022 [CTSC2012]熟悉的文章(广义SAM+决策单调性)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4022 [CTSC2012]熟悉的文章(广义SAM+决策单调性)相关的知识,希望对你有一定的参考价值。

LINK

显然 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] [isufi+1,i]能匹配上原串

先继承上一次的状态 f [ i ] = f [ i − 1 ] f[i]=f[i-1] f[i]=f[i1]

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=isufi,r=iL0

那么有 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]+ij}

这部分维护一个 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)

90分代码LINK

复杂度还不够优秀,进一步观察到每次只需要从 [ i − s u f i , i − L 0 ] [i-suf_i,i-L_0] [isufi,iL0]转移而来

发现具有决策单调性,因为 i − s u f i i-suf_i isufi单调递增,于是可以套一个单调队列求区间最值

#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+决策单调性)的主要内容,如果未能解决你的问题,请参考以下文章

P4022 [CTSC2012]熟悉的文章

洛谷P4022熟悉的文章

[BZOJ2806][CTSC2012]熟悉的文章(Cheat)

[CTSC2012]熟悉的文章(后缀自动机+动态规划)

BZOJ2806[Ctsc2012]Cheat 广义后缀自动机+二分+单调队列优化DP

BZOJ2806CTSC2012Cheat 广义后缀自动机+二分+Dp