P4022 [CTSC2012]熟悉的文章

P4022 [CTSC2012]熟悉的文章

题目大意

$m$个文本,$n$个模式串

对于每个模式串:

求最大L使得该串能分解成不小于$L$的子串,且在文本中出现的长度不小于该串总长度的$90%$

建广义后缀树

对于$L_1$,$L_2$两种情况,$L_1>L_2$,如果$L_1$符合,显然$L_2$一定符合

发现没有,答案是单调的,用二分$check$

$dp_i$为前$i$个字符能匹配的最大长度

$dp_i=max\{dp[j]+i-j\}$转换后$dp_i=max\{(dp[j]-j)+i\}$用单调队列维护就行

My complete code: 

#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
typedef long long LL;
const LL maxn=400000;
LL n,m,nod=1,last,Len;
LL len[maxn],son[maxn][26],fail[maxn],val[maxn],dp[maxn];
char s[maxn];
inline void Insert(LL c){
	LL p=last,np=++nod;
	last=np;
	len[np]=len[p]+1;
	while(p&&!son[p][c]){
		son[p][c]=np;
		p=fail[p];
	}
	if(!p)
	    fail[np]=1;
	else{
		LL q=son[p][c];
		if(len[q]==len[p]+1)
		    fail[np]=q;
		else{
			LL nq=++nod;
			len[nq]=len[p]+1;
			memcpy(son[nq],son[q],sizeof(son[q]));
			fail[nq]=fail[q];
			fail[q]=fail[np]=nq;
			while(p&&son[p][c]==q){
				son[p][c]=nq;
				p=fail[p];
			}
		}
	}
}
inline void Match(){
	LL now=1,l=0;
	for(LL i=1;i<=Len;++i){
		LL c=s[i]-'0';
		while(now&&!son[now][c]){
			now=fail[now];
			l=len[now];
		}
		if(now){
			now=son[now][c];
			++l;
		}else{
			now=1;
			l=0;
		}
		val[i]=l;
	}
}
inline bool check(LL L){
	LL head=1,tail=0;
	LL que[maxn];
	for(LL i=1;i<=L-1;++i)
	    dp[i]=0;
	for(LL i=L;i<=Len;++i){
		while(head<=tail&&dp[que[tail]]-que[tail]<dp[i-L]-(i-L))
		    --tail;
		que[++tail]=i-L;
		while(head<=tail&&que[head]<i-val[i])
		    ++head;
		dp[i]=dp[i-1];
		if(head<=tail)
		    dp[i]=max(dp[i],dp[que[head]]-que[head]+i);
	}
	return dp[Len]*10>=Len*9;
}
inline LL Solve(){
	LL l=1,r=Len,ans=0;
	Match();
	while(l<=r){
		LL mid=(l+r)>>1;
		if(check(mid)){
			ans=mid;
			l=mid+1;
		}else
		    r=mid-1;
	}
	return ans;
}
int main(){
	scanf("%lld%lld",&n,&m);
	while(m--){
		scanf(" %s",s+1);
		Len=strlen(s+1);
		last=1;
		for(LL i=1;i<=Len;++i)
		    Insert(s[i]-'0');
	}
	while(n--){
		scanf(" %s",s+1);
		Len=strlen(s+1);
		printf("%lld\n",Solve());
	}
}

  

猜你喜欢

转载自www.cnblogs.com/y2823774827y/p/10122570.html