[BZOJ2806] [CTSC2012] Cheat

Description

img

img

Input

第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库
的行数
接下来M行的01串,表示标准作文库
接下来N行的01串,表示N篇作文

Output

N行,每行一个整数,表示这篇作文的Lo 值。

Sample Input

1 2
10110
000001110
1011001100

Sample Output

4

Solution

神题。

首先把模式串建出广义后缀自动机,由于我不会这个就只好把串用特殊符号连起来然后建单串\(SAM\)了...这玩意比较容易被卡的,这题字符集很小串长也很小就没啥问题。

然后对于匹配串预处理出一个\(maxlen[i]\)表示第\(i\)位结尾的最长公共子串长度,这个可以在自动机上跑出来,具体的,沿着自动机走,如果走到了一个非法状态就条\(parent\)状态转移回去,然后长度就变成了\(maxl+1\)

注意到\(L\)是满足可二分性的,所以可以先二分答案。

然后对于这种切成一段段的区间的问题可以考虑一个\(O(n^2)\)\(dp\)加玄学优化。

这里就设\(f[i]\)表示考虑了前\(i\)个点的答案,然后可以得到一个很显然的转移:

\[f[i]=\max(f[i-1],\max_{j=i-maxlen[i]}^{i-L}\{f[j]+i-j\}) \]

注意到决策点是单调不降的,并且\(i\)也是单增的,那么就可以用一个单调队列来维护\(f[j]-j\)

时间复杂度\(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 2e6+10;

char s[maxn];
int n,m,qs=1,cnt=1,lstp=1,len,head,tail;
int par[maxn],tr[maxn][3],ml[maxn],mxlen[maxn],f[maxn];

void append(int c) {
	int p=lstp,np=++cnt;ml[np]=ml[p]+1;lstp=np;
	for(;p&&tr[p][c]==0;p=par[p]) tr[p][c]=np;
	if(!p) return par[np]=qs,void();
	int q=tr[p][c];
	if(ml[p]+1<ml[q]) {
		int nq=++cnt;ml[nq]=ml[p]+1;
		memcpy(tr[nq],tr[q],sizeof tr[nq]);
		par[nq]=par[q],par[q]=par[np]=nq;
		for(;p&&tr[p][c]==q;p=par[p]) tr[p][c]=nq;
	} else par[np]=q;
}

int q[maxn];

void get_mxlen() {
	int now=qs,res=0;
	for(int i=1,v;i<=len;i++) {
		if(tr[now][v=s[i]-'0']) now=tr[now][v],res++;
		else {
			for(;now&&tr[now][v]==0;now=par[now]);
			if(!now) now=qs,res=0;else res=ml[now]+1,now=tr[now][v];
		}
		mxlen[i]=res;
	}
}

int check(int L) {
	head=1,tail=0;
	for(int i=0;i<L;i++) f[i]=0;
	for(int i=L;i<=len;i++) {
		f[i]=f[i-1];
		while(head<=tail&&f[q[tail]]-q[tail]<f[i-L]-i+L) tail--;
		q[++tail]=i-L;
		while(head<=tail&&q[head]<i-mxlen[i]) head++;
		if(head<=tail) f[i]=max(f[i],f[q[head]]-q[head]+i);
	}return len*9<=f[len]*10;
}

int main() {
	read(n),read(m);
	for(int i=1;i<=m;i++) {
		scanf("%s",s+1);int l=strlen(s+1);append(2);
		for(int j=1;j<=l;j++) append(s[j]-'0');
	}
	for(int T=1;T<=n;T++) {
		scanf("%s",s+1);
		int l=0,r=strlen(s+1),mid,ans=0;
		len=r;get_mxlen();
		while(l<=r) if(check(mid=(l+r)>>1)) l=mid+1,ans=mid;else r=mid-1;
		write(ans);
	}
	return 0;
}
posted @ 2019-02-26 11:51  Hyscere  阅读(130)  评论(0编辑  收藏  举报