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}}\) ,则有
由于要算出所有节点的 \(\text{endpos}\) 集合的答案,而一个节点 \(u\) 的 \(\text{endpos}\) 集合是所有子节点的 \(\text{endpos}\) 的父集,所以要支持合并父亲和儿子节点的答案。根据上面的结论,可以根据 \(\text{dfs}\) 序来建立线段树,维护区间的 \(\text{dfs}\) 序最小和最大的节点以及答案,直接线段树合并,合并时 \(LCA\) 用欧拉序和 \(\text{ST}\) 表求即可。
总时间复杂度 \(O(|s|\log |s|)\)。
#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;
}