cunzai_zsy0531

关注我

CF427D Match & Catch 题解

题面

虽然 \(n,m\leq 5000\),但是这题是 \(O((n+m)|\Sigma|)\) 的。首先建 \(S\) 的 SAM,拿 \(T\) 上去跑可以得到 \(T\) 每个前缀的匹配区间(在 \(S\) 中只出现一次)。接着建 \(T\) 的 SAM,也可以求出每个前缀在 \(T\) 中只出现一次的区间。做一个区间交,取一个全局 \(\min\) 即可。

点击查看代码
inline void clearvector(std::vector<int> &a){std::vector<int> b;std::swap(a,b);}
const int N=5000+13;
char s[N],t[N];
int nxt[N<<1],len[N<<1],ptot,lastpos,cnt[N<<1];
std::vector<int> son[N<<1],zrzak;
std::vector<int> G[N<<1];
inline void clear(){
	for(int i=1;i<=ptot;++i) clearvector(son[i]),clearvector(G[i]),cnt[i]=nxt[i]=len[i]=0;
	son[1].resize(26),zrzak.resize(26);
	ptot=lastpos=1;
}
inline int newpos(std::vector<int> nson,int nlen){return len[++ptot]=nlen,std::swap(son[ptot],nson),ptot;}
inline void insert(int c){
	int p=lastpos,u=newpos(zrzak,len[p]+1);cnt[u]=1;
	while(p&&!son[p][c]) son[p][c]=u,p=nxt[p];
	lastpos=u;
	if(!p) return nxt[u]=1,void();
	int d=son[p][c];
	if(len[d]==len[p]+1) nxt[u]=d;
	else{
		int v=newpos(son[d],len[p]+1);
		nxt[v]=nxt[d],nxt[u]=nxt[d]=v;
		while(p&&son[p][c]==d) son[p][c]=v,p=nxt[p];
	}
}
void dfs(int u){for(auto v:G[u])dfs(v),cnt[u]+=cnt[v];}
pii a[N];
int main(){
	clear();
	read(s+1),read(t+1);
	int n=strlen(s+1),m=strlen(t+1);
	for(int i=1;i<=n;++i) insert(s[i]-'a');
	for(int i=2;i<=ptot;++i) G[nxt[i]].pb(i);
	dfs(1);
	for(int i=1,now=1,l=0;i<=m;++i){
		int c=t[i]-'a';
		while(now!=1&&!son[now][c]) now=nxt[now],l=len[now];
		if(son[now][c]) now=son[now][c],++l;
		if(l&&cnt[now]==1) a[i]=mp(len[nxt[now]]+1,l);
	}
	clear();
	for(int i=1;i<=m;++i) insert(t[i]-'a');
	for(int i=2;i<=ptot;++i) G[nxt[i]].pb(i);
	dfs(1);
	int ans=INF;
	for(int i=1,now=1,l=0;i<=m;++i){
		int c=t[i]-'a';
		while(now!=1&&!son[now][c]) now=nxt[now],l=len[now];
		if(son[now][c]) now=son[now][c],++l;
		if(cnt[now]==1){
			int l1=a[i].fi,r1=a[i].se,l2=len[nxt[now]]+1,r2=l;
			if(l1>l2) swap(l1,l2),swap(r1,r2);
			if(l2&&l2<=r1) chkmin(ans,l2);
		}
	}
	if(ans==INF) ans=-1;
	println(ans);
	return 0;
}
posted @ 2022-05-18 21:17  cunzai_zsy0531  阅读(32)  评论(0编辑  收藏  举报