Codeforces 1276F. Asterisk Substrings (3400)

给定一个全由小写字母组成的字符串 \(s\),设 \(t_i\) 表示将 \(s\) 中的第 \(i\) 个字符替换成 \(*\) 后得到的字符串,求字符串集合 \(\{s,t_1,\ldots,t_{|s|}\}\) 的本质不同的子串数量(包含空串)。
\(1\le |s|\le 10^5\)


首先答案的构成一定是 \(\{\empty,*,s*,*s,s*t\}\) 五种形式,对于前四种,直接用 \(\text{SAM}\) 算出。

考虑第五种形式,枚举字符串 \(s\) 中哪一位被替换成 \(*\) 再计算是不可取的,因为很难去重。那么尝试枚举 \(s\),可以得到 \(s\)\(\text{endpos}\) 集合。此时 \(t\) 一定是从任意一个 \(\text{endpos}+2\) 的位置开始的字符串,也就是 \(s[\text{endpos}+2:|s|]\) 的前缀。那么所有的 \(t\) 的数量就为 \(\{s[p+2:|s|]\mid p\in \text{endpos}(s)\}\) 的本质不同的前缀数量。

前缀显然不能直接做,那么考虑建立 \(s\) 的反串的 \(\text{SAM}\),此时反串的 \(\text{SAM}\) 上的某个节点 \(u\) 合法当且仅当存在至少一个 \(p\in \text{endpos}(s)\),使得表示 \(s[p+2:|s|]\) 的节点在 \(u\) 的子树内。所以合法的 \(u\) 的数量就是每个表示 \(s[p+2:|s|]\ (p\in\text{endpos}(s))\) 的节点到根的路径的并的大小。

\(T=\{u\mid \exist p\in V,p\in \text{subtree}_{u}\}\)\(v_1,v_2\ldots v_k\)\(V\) 中的元素,\(\text{dfn}_{u}\)\(u\)\(\text{dfs}\) 序,\(\text{dep}_u\)\(u\) 的深度,\(\forall i<k,\text{dfn}_{v_i}<\text{dfn}_{v_{i+1}}\) ,则有

\[|T|=\sum_{i=1}^{k} \text{dep}_{v_i}-\sum_{i=2}^{k} \text{dep}_{LCA(v_{i-1}\ ,v_i)} \]

由于要算出所有节点的 \(\text{endpos}\) 集合的答案,而一个节点 \(u\)\(\text{endpos}\) 集合是所有子节点的 \(\text{endpos}\) 的父集,所以要支持合并父亲和儿子节点的答案。根据上面的结论,可以根据 \(\text{dfs}\) 序来建立线段树,维护区间的 \(\text{dfs}\) 序最小和最大的节点以及答案,直接线段树合并,合并时 \(LCA\) 用欧拉序和 \(\text{ST}\) 表求即可。

总时间复杂度 \(O(|s|\log |s|)\)

\(\color{blue}{\text{code}}\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e5+5;
struct Node{
	int son[26],fa,len;
	Node(){memset(son,sizeof son,0),fa=len=0;}
};
int n,cnt,T,dfn[N],rt[N],st[N+N][20],L[N*18],R[N*18],ls[N*18],rs[N*18];
ll ans,sum[N*18];char s[N];
inline int LCAL(int x,int y){
	if(!x||!y)return 0;
	int k=log2(y-x+1);
	return min(st[x][k],st[y-(1<<k)+1][k]);
}
inline void upd(int x){
	sum[x]=sum[ls[x]]+sum[rs[x]]-LCAL(R[ls[x]],L[rs[x]]);
	L[x]=L[ls[x]?ls[x]:rs[x]],R[x]=R[rs[x]?rs[x]:ls[x]];
}
inline void insert(int &u,int l,int r,int x){
	u=++cnt;
	if(l==r)return (void)(L[u]=R[u]=x,sum[u]=st[x][0]);
	int mid=l+r>>1;
	x<=mid?insert(ls[u],l,mid,x):insert(rs[u],mid+1,r,x);
	upd(u);
}
inline int merge(int u,int v){
	if(!u||!v)return u+v;
	if(!ls[u]&&!rs[u])return u;
	ls[u]=merge(ls[u],ls[v]),rs[u]=merge(rs[u],rs[v]),upd(u);
	return u;
}
struct Suffix_Autumaton{
	int last=1,tot=1,pos[N];
	Node node[N];vector<int>G[N];
	inline void add(int c,int id){
		int p=last,nw=pos[id]=last=++tot;node[nw].len=node[p].len+1;
		for(;p&&!node[p].son[c];p=node[p].fa)node[p].son[c]=nw;
		if(!p)node[nw].fa=1;
		else{
			int q=node[p].son[c];
			if(node[q].len==node[p].len+1)node[nw].fa=q;
			else{
				int xq=++tot;
				node[xq]=node[q];node[xq].len=node[p].len+1;node[q].fa=node[nw].fa=xq;
				for(;p&&node[p].son[c]==q;p=node[p].fa)node[p].son[c]=xq;
			}
		}
	}
	inline void dfs1(int x){
		st[dfn[x]=++T][0]=node[x].len;
		for(auto y:G[x])dfs1(y),st[++T][0]=node[x].len;
	}
	inline void dfs2(int x){
		for(auto y:G[x])dfs2(y),rt[x]=merge(rt[x],rt[y]);
		ans+=(x>1)*(node[x].len-node[node[x].fa].len)*sum[rt[x]];
	}
	inline void ST(){
		for(int i=2;i<=tot;++i)G[node[i].fa].emplace_back(i);
		dfs1(1);
		for(int j=1;j<=19;++j)
			for(int i=1;i<=T-(1<<j)+1;++i)
				st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
	}
	inline void solve(){
		for(int i=2;i<=tot;++i)G[node[i].fa].emplace_back(i);
		dfs2(1);
	}
}LS,RS;
int main(){
	scanf("%s",s+1),n=strlen(s+1);
	for(int i=1;i<=n;++i)LS.add(s[i]-'a',i),ans+=((i<n)+1)*(i-LS.node[LS.node[LS.last].fa].len);
	for(int i=n;i;--i)RS.add(s[i]-'a',i),ans+=(i>1)*(n-i+1-RS.node[RS.node[RS.last].fa].len);
	RS.ST();
	for(int i=1;i<n-1;++i)insert(rt[LS.pos[i]],1,T,dfn[RS.pos[i+2]]);
	LS.solve();
	return printf("%lld\n",ans+2),0;
}
posted @ 2022-06-10 20:52  Samsara-soul  阅读(36)  评论(0编辑  收藏  举报