字符串【下】

发布时间 2023-12-24 22:44:10作者: q(x)
\(\color{white}{P5546 最长公共子串}\)

把字符串拼起来,也就是用 # 连接,然后在上面做最长重复且属于所有串的后缀均出现过的子串。也就是满足以下条件的子串

  1. 重复过

  2. 其中包含的后缀可以覆盖所有的串

这样的子串是合格的。要求求得一个最长的串满足上述条件。

最长也就是要求最大化 \(\min\{h[k]\} _{i < k \leq j}\) 而且要使第二个条件满足。

上面式子是单调不升的,所以最小化 \(j-i\) ,显然这是可以通过 two-pointer 维护的,而 2. 条件可以用类似莫队或者用前缀和维护,至于查询 \(\min\) ,滑动窗口、线段树、st 均可。这里选用线段树。因为很好写。

然后直接算就行了。

复杂度看似十分玄学,实际上瓶颈还是在于前面求 \(h\) 数组的 \(\mathcal{O(nlog^2n)}\),写出来就可以 \(ac.\) 主要要注意上面的 \(<\) 符号,很容易被阴。

$\text{code}$
// Qx 's data
#include <bits/stdc++.h>
#define ll long long
using std::cin;
using std::cout;
using std::cerr;
const int N=20005,Time=10,inf=0x3f3f3f3f;
int T,n,w;
char ch[N*5];
int ed[Time+5];
int rk[N*10],sa[N*10],rk0[N*10];
int h[N*5];
char s[10][N];
int col[N*10],vis[N*10];
inline int gf(int x){
	return vis[x];
}
inline void _sa(void){
	for(int i=1;i<=n;++i)
		rk[i]=ch[i],sa[i]=i;
	for(w=1;w<=n;w<<=1){
		std::stable_sort(sa+1,sa+n+1,[](int x,int y){
			return rk[x]==rk[y]?rk[x+w]<rk[y+w]:rk[x]<rk[y];
		});
		int cnt=0;
		memcpy(rk0,rk,sizeof rk);
		for(int i=1;i<=n;++i){
			if(rk0[sa[i]]==rk0[sa[i-1]] &&
				rk0[sa[i]+w]==rk0[sa[i-1]+w])
			rk[sa[i]]=cnt;
			else
			rk[sa[i]]=++cnt;
		}
	}
}
inline void _h(void){
	int k=0;
	for(int i=1;i<=n;++i)
		rk[sa[i]]=i;
	for(int i=1;i<=n;++i){
		if(k) --k;
		int j=sa[rk[i]-1];
		while(ch[i+k]==ch[j+k] && ch[i+k]!='#') ++k;
		h[rk[i]]=k;
	}
	return;
}
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)
#define lson ls,l,mid
#define rson rs,mid+1,r
struct segtree{
	int tree[N*40];
	inline void pushup(int rt,int l=0,int r=0){
		return tree[rt]=std::min(tree[ls],tree[rs]),void();
	}
	int query(int rt,int l,int r,int L,int R){
		if(L<=l && r<=R) return tree[rt];
		int res=inf;
		if(L<=mid) res=std::min(res,query(lson,L,R));
		if(R> mid) res=std::min(res,query(rson,L,R));
		return res;
	}
	void Build(int rt,int l,int r){
		if(l==r) return tree[rt]=h[l],void();
		Build(lson) , Build(rson);
		return pushup(rt,l,r),void();
	}
}G;
int l=0,r=0,cs=0,ans=-inf;
inline void add(int x){
	if(!x) return;
	++col[x];
	if(col[x]==1) ++cs;
}
inline void del(int x){
	if(!x) return;
	--col[x];
	if(col[x]==0) --cs;
}
inline void buildvis(void){
	for(int i=1;i<=T;++i){
		for(int j=ed[i-1]+1;j<ed[i];++j)
			vis[rk[j]]=i;
	}
		
}
inline void two_pointer(void){
	_sa(),_h();
	G.Build(1,1,n);
	buildvis();
	l=r=1;
	add(gf(1));
	while(r<=n){
		if(cs==T){
			while(cs==T)del(gf(l)),++l;
			--l,add(gf(l));
			ans=std::max(ans,G.query(1,1,n,l+1,r));
		}
		++r;
		add(gf(r));
	}
	printf("%d\n",ans);
} 
int main(){
	cin>>T;
	for(int i=1;i<=T;++i) cin>>(s[i]+1);
	for(int i=1;i<=T;++i) ed[i]=ed[i-1]+strlen(s[i]+1)+1;
	for(int i=1;i<=T;++i){
		int len=strlen(s[i]+1);
		for(int j=1;j<=len;++j)
			ch[++n]=s[i][j];
		ch[++n]='#';
	}
	--n;
	two_pointer(); 
}