[CTSC2012]熟悉的文章

Posted by Dispwnl on January 8, 2019

题目

题目描述

阿米巴是小强的好朋友。

在小强眼中,阿米巴是一个作文成绩很高的文艺青年。为了获取考试作文的真谛,小强向阿米巴求教。阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么觉得熟悉,仿佛是某些范文拼拼凑凑而成的。小强不禁向阿米巴投去了疑惑的眼光,却发现阿米巴露出了一个狡黠的微笑。

为了有说服力地向阿米巴展示阿米巴的作文是多么让人觉得“眼熟”,小强想出了一个评定作文 “熟悉程度”的量化指标:L 0 .小强首先将作文转化成一个 01 串。之后,小强搜集了各路名家的文章,同样分别转化成 01 串后,整理出一个包含了 M 个 01 串的“ 标准作文库 ”。

小强认为:如果一个 01 串长度不少于 L 且在 标准作文库 中的某个串里出现过(即,它是 标准作文库 的 某个串 的一个 连续子串 ),那么它是“ 熟悉 ”的。对于一篇作文(一个 01 串)A,如果能够把 A 分割成若干段子串,其中“ 熟悉 ” 的子串的 长度 总 和 不少于 A 总 长度的 90%,那么称 A 是 “ 熟悉的文章 ”。 L 0 是 能够让 A 成为 “ 熟悉的文章 ” 的 所有 L 的最大值 (如果不存在这样的 L,那么规定 L 0 =0)。

举个例子:

小强的作文库里包含了如下 2 个字符串:

10110
000001110

有一篇待考察的作文是:

1011001100

小强计算出这篇作文 L 的最大值是 4,因为待考察的作文可以视作’10110’+’0110’+’0’,其中’10110’和’0110’被判定为 “ 熟悉 ” 的。而当 L = 5 或是更大的时候,不存在符合题意的分割方法。所以,这篇作文的 L 0 = 4。小强认为阿米巴作文的 L 0 值比其他同学的明显要大。请你帮他验证一下。

输入输出格式

输入格式:

输入文件 cheat.in 第一行是两个整数 N, M,表示待检查的作文数量,和小强的标准作文库的行数。

接下来是 M 行的 01 串,表示标准作文库。

接下来是 N 行的 01 串,表示 N 篇作文。

输出格式:

输出文件 cheat.out 包含 N 行,每一行包含一个整数,表示该篇作文的 L 0 值。

输入输出样例

输入样例#1:

1 2
10110
000001110
1011001100

输出样例#1:

4

说明

对于 30%的测试数据,输入文件的长度不超过 1000 字节。

对于 50%的测试数据,输入文件的长度不超过 61000 字节。

对于 80%的测试数据,输入文件的长度不超过 250000 字节。

对于 100%的测试数据,输入文件的长度不超过 1100000 字节。

题解

假设已经知道每个点往前能最长匹配多少字符$Lmax$(这个可以用$SAM$搞出来),现在有一个$L$,要判断这个$L$是否合法,不难想到用$f_i$表示前$i$位匹配的最长长度

则有:$f_i=max(f_{j-1}+i-j+1),j\ge i-Lmax_i+1,j\ge i-L+1$

发现如果只有$j\ge i-L+1$,这个就可以用单调队列优化了

可以发现$i-Lmax_i+1$是单调不减的,假设对于某个$i$有$j$,使得$j>i,Lmax_j-j+1< Lmax_i-i+1$,对于$Lmax_j-j+1$到$Lmax_i-i+1$这部分,从$i$匹配肯定也可以匹配上,得证

所以还是可以用单调队列优化了

对于每个$L$,发现某个大的$L$成立,小于ta的$L$肯定也可以成立,所以可以二分$L$了

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
using namespace std;
const int MAX=2.2e6+5;
int N,M,n;
char a[MAX];
struct SAM{
	int l,r,L;
	int len[MAX],fa[MAX],L_max[MAX],qu[MAX],f[MAX];
	int son[MAX][2];
	SAM() {l=r=1;}
	void ins(int x)
	{
		int tt=r;
		len[r=++l]=++L;
		for(;tt&&!son[tt][x];tt=fa[tt])
		  son[tt][x]=r;
		if(!tt) return void(fa[r]=1);
		int qwq=son[tt][x];
		if(len[qwq]==len[tt]+1) return void(fa[r]=qwq);
		len[++l]=len[tt]+1,fa[l]=fa[qwq],fa[qwq]=fa[r]=l,son[l][0]=son[qwq][0],son[l][1]=son[qwq][1];
		for(int i=tt;son[i][x]==qwq;i=fa[i])
		  son[i][x]=l;
	}
	void Init(int _n)
	{
		for(int i=1,x=1,_L=0;i<=_n;++i)
		  {
		  	if(son[x][a[i]-'0']) x=son[x][a[i]-'0'],++_L;
		  	else
		  	{
		  		while(x&&!son[x][a[i]-'0']) x=fa[x];
		  		if(!x) x=1,_L=0;
		  		else _L=len[x]+1,x=son[x][a[i]-'0'];
			}
			L_max[i]=_L;
		  }
	}
	bool look(int mid,int _n)
	{
		for(int i=1;i<mid;++i)
		  f[i]=0,qu[i]=0;
		for(int i=mid,hl=1,tl=1;i<=_n;++i)
		  {
		  	f[i]=f[i-1];
		  	while(hl<=tl&&qu[hl]<i-L_max[i]) ++hl;
		  	if(hl<=tl) f[i]=max(f[i],f[qu[hl]]+i-qu[hl]);
			while(hl<=tl&&f[qu[tl]]-qu[tl]<=f[i-mid+1]-i+mid-1) --tl;
		  	qu[++tl]=i-mid+1;
		  }
		return f[_n]*10>=_n*9;
	}
	int Solve(int _n)
	{
		int l=1,r=_n,ans=0;
		Init(_n);
		while(l<=r)
		{
			int mid(l+r>>1);
			if(look(mid,_n)) ans=mid,l=mid+1;
			else r=mid-1;
		}
		return ans;
	}
}_S;
int main()
{
	scanf("%d%d",&N,&M);
	for(int i=1,_n;i<=M;++i,_S.r=1,_S.L=0)
	  {
	  	scanf("%s",a+1),_n=strlen(a+1);
	  	for(int j=1;j<=_n;++j)
	  	  _S.ins(a[j]-'0');
	  }
	for(int i=1,_n;i<=N;++i)
	  scanf("%s",a+1),_n=strlen(a+1),printf("%d\n",_S.Solve(_n));
	return 0;
}