P4022 [CTSC2012] 熟悉的文章 题解

P4022 [CTSC2012] 熟悉的文章

容易发现,能和 SAM 搞在一起的东西还挺多的。

首先要求最大的 L,显然这个 L 是满足单调性的:若 L 合法,则 L1,L2, 都合法。所以考虑二分这个 L

首先看到这个序列分段,应该想到经典的 DP:设 fi 表示到第 i 个位置最长能匹配多长的字符串,那么显然有转移方程

fi=max{fj+ij} ,j[iwi,iL]

其中 wi 是以 i 为结尾的字符串出现在模板串中的最长长度。用 SAM 处理这个东西应该是基操,考虑到题目中给出了多个模板串,所以套广义 SAM 的板子就可以处理出它。

现在复杂度是 O(n2) 的,但是注意到随着 i 的增加,j 的合法取值范围是一个滑动窗口,可以采用单调队列优化,复杂度变为 O(n)

#include<bits/stdc++.h>
using namespace std;

constexpr int MAXN=2.2e6+5;
int N,M,n,w[MAXN];
struct{
	int tot=1;
	struct Trie{
		int fa,c,s[2];
	}t[MAXN];
	void ins(const string&s){
		int p=1;
		for(int c:s){
			c-='0';
			if(!t[p].s[c]){
				t[p].s[c]=++tot;
				t[tot].fa=p,t[tot].c=c;
			}
			p=t[p].s[c];
		}
	}
}T;
struct{
	int tot=1;
	struct SAM{
		int len,fa,s[2];
	}sam[MAXN];
	int ins(int x,int lst){
		sam[++tot].len=sam[lst].len+1;
		int pos=lst,ch=T.t[x].c;
		lst=tot;
		while(pos&&!sam[pos].s[ch]){
			sam[pos].s[ch]=tot;
			pos=sam[pos].fa;
		}
		if(!pos) sam[tot].fa=1;
		else{
			int p=pos,q=sam[pos].s[ch];
			if(sam[p].len+1==sam[q].len) sam[tot].fa=q;
			else{
				sam[++tot]=sam[q];
				sam[tot].len=sam[p].len+1;
				sam[q].fa=sam[lst].fa=tot;
				while(pos&&sam[pos].s[ch]==q){
					sam[pos].s[ch]=tot;
					pos=sam[pos].fa;
				}
			}
		}
		return lst;
	}
	void build(){
		queue<pair<int,int>>q;
		for(int i=0;i<2;i++) if(T.t[1].s[i]) q.emplace(T.t[1].s[i],1);
		while(!q.empty()){
			int u=q.front().first,lst=q.front().second;
			q.pop();
			int now=ins(u,lst);
			for(int i=0;i<2;i++) if(T.t[u].s[i]) q.emplace(T.t[u].s[i],now);
		}
	}
	void initw(const string&s){
		int lst=0,pos=1;
		for(int i=1;i<=n;i++){
			int c=s[i]-'0';
			if(sam[pos].s[c]) lst++,pos=sam[pos].s[c];
			else{
				while(pos&&!sam[pos].s[c]) pos=sam[pos].fa;
				if(!pos) lst=0,pos=1;
				else lst=sam[pos].len+1,pos=sam[pos].s[c];
			}
			w[i]=lst;
		}
	}
	int q[MAXN],f[MAXN];
	bool check(int x){
		int h=1,t=0;
		memset(f,0,x<<2);
		for(int i=x;i<=n;i++){
			f[i]=f[i-1];
			while(h<=t&&f[q[t]]-q[t]<=f[i-x]-i+x) t--;
			q[++t]=i-x;
			while(h<=t&&q[h]<i-w[i]) h++;
			if(h<=t) f[i]=max(f[i],f[q[h]]+i-q[h]);
		}
		return n*0.9<=f[n];
	}
}S;

int main(){
	cin.tie(nullptr)->sync_with_stdio(0);
	cin>>N>>M;
	for(int i=1;i<=M;i++){
		string s;
		cin>>s;
		T.ins(s);
	}
	S.build();
	for(int i=1;i<=N;i++){
		string s;
		cin>>s;
		n=s.size();
		s=' '+s;
		S.initw(s);
		int l=0,r=n,ans=0;
		while(l<=r){
			int mid=(l+r)>>1;
			if(S.check(mid)) ans=mid,l=mid+1;
			else r=mid-1;
		}
		cout<<ans<<'\n';
	}
	return 0;
}
posted @   Laoshan_PLUS  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示