$\text{SAM}$ 是一个十分 $\text{bdd}$ 的数据结构,然鹅我并不能对它下一个清晰(且我能看懂)的定义……
每个点记录了一个原串的子串集合:
$\text{1.}$ 子串出现的末尾位置集合相同.
$\text{2.}$ 子串是某些前缀的连续后缀,即长度为一个连续的区间.
$\text{3.}$ 集合中所有串都是(集合中)最长串的后缀.
$\text{4.}$ 每个点包含的子串集合互不相交.
每个点保存的信息有:
$len$ :该点表示的串的集合中长度的最大值,最小值则 $=len(fa)+1$ .
$fa$ : 绿色的边指向的点, $fa$ 表示的子串集合都为 $p$ 中串的后缀,且长度均小于最短的串.每个点向 $fa$ 连边可以得到一棵树.
$son$ :蓝色的边指向的点, $son[c]$ 表示 $p$ 中的串后面接上字符 $c$ 后的状态.
$siz$ :子串出现的末尾位置 $endpos$ 集合的大小,可通过拓扑排序(易理解)或某种更优美的方法求,有时还可以顺便记下 $dfn$ (拓扑排序后的顺序).
$pos$ :子串出现的末尾位置中的最后一个?(大概)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdlib>
#include <queue>
#include <cmath>
#define pr(p) printf("%lld\n",p)
const int oo=2139063143;
const int N=2010000;
const int mod=1000000007;
using namespace std;
typedef long long LL;
typedef double db;
inline void sc (int &x)
{
x=0; static int p; p=1; static char c; c=getchar();
while (!isdigit(c)) { if (c=='-') p=-1; c=getchar(); }
while ( isdigit(c)) { x=(x<<1)+(x<<3)+(c-48); c=getchar(); }
x*=p;
}
struct SAM { int len,fa,son[28],siz; }sam[N<<1]; int tot=1;
#define len(p) sam[p].len
#define fa(p) sam[p].fa
#define son(p) sam[p].son
#define siz(p) sam[p].siz
int pre=1;
void addchar (int c)
{
int f=pre,p=++tot; pre=p;
len(p)=len(f)+1,siz(p)=1;
while (f&&!son(f)[c]) son(f)[c]=p,f=fa(f);
if (!f) { fa(p)=1; return ; }
int x=son(f)[c];
if (len(x)==len(f)+1) { fa(p)=x; return ; }
int y=++tot;
len(y)=len(f)+1,fa(y)=fa(x),fa(x)=fa(p)=y;
memcpy (son(y),son(x),sizeof (son(y)));
while (f&&son(f)[c]==x) son(f)[c]=y,f=fa(f);
}
char s[N];
queue <int> q;
int deg[N];
int main()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
scanf ("%s",s+1);
int n=strlen (s+1);
for (int i=1; i<=n; i++)
addchar (s[i]-'a'+1);
for (int i=1; i<=tot; i++)
++deg[fa(i)];
for (int i=1; i<=tot; i++)
if (!deg[i]) q.push (i);
LL ans=0;
while (q.size ())
{
int u=q.front (); q.pop ();
if (siz(u)> 1) ans=max (ans,1LL*siz(u)*len(u));
siz(fa(u))+=siz(u);
--deg[fa(u)];
if (!deg[fa(u)]) q.push (fa(u));
}
pr(ans);
return 0;
}
求两个串的最长公共子串.
对第一个建 $\text{SAM}$ ,再将第二个放在上面沿着 $son$ 边跑,若跑到没有对应的出边了,可以不断跑向 $fa$ ,( $fa$ 是 $p$ 的后缀,而且出现位置更多),直到有对应的出边为止,并对于第二个串的每个前缀都更新一次答案.
int ans=0,cur=1,cnt=0;
for (int i=1; i<=m; i++)
{
int c=t[i]-'a'+1;
while (fa(cur)&&!son(cur)[c]) cur=fa(cur),cnt=len(cur);
if (son(cur)[c]) cur=son(cur)[c],++cnt;
ans=max (ans,cnt);
}
pr(ans);
求若干串在另一个长串 $\text{S}$ 中各自作为子串出现的次数,但匹配的方式从完全相等变成了“循环同构”.
首先可将询问串复制一遍粘在后面,但要去掉最后一个字符(避免重复).
利用 $\text{S}$ 的 $\text{SAM}$ 可求出对于询问串的每个前缀的最长匹配长度.
这个长度 $len$ 可能超过了原询问串的长度,此时可以向 $fa$ 跳,直到 $len(fa)$ 比原询问串长,才能保证不漏.因为 $len$ 若比原串长度长,则会少计算一些满足”匹配长度长于原串,却无法达到 $len$ “的串.
int T; sc(T);
for (int kind=1; kind<=T; kind++)
{
scanf ("%s",t+1);
int len=strlen (t+1);
for (int i=len+1; i< len<<1; i++)
t[i]=t[i-len];
int cur=1,cnt=0,ans=0;
for (int i=1; i< len<<1; i++)
{
int c=t[i]-'a'+1;
while (fa(cur)&&!son(cur)[c]) cur=fa(cur),cnt=len(cur);
if (son(cur)[c]) cur=son(cur)[c],++cnt;
if (cnt>=len)
{
while (len(fa(cur))>=len) cur=fa(cur),cnt=len(cur);
if (vis[cur]!=kind) ans+=siz(cur),vis[cur]=kind;
}
}
pr(ans);
}
求两个串的最短公共唯一子串(仅在两个串中分别各出现 $\text{1}$ 次).
对 $\text{A}$ 建立 $\text{SAM}$ ,只在 $\text{A}$ 中出现 $\text{1}$ 次的串肯定是 $siz$ 为 $\text{1}$ 的.
将 $\text{B}$ 在 $\text{SAM}$ 上面跑,得到 $\text{B}$ 在每个状态的出现次数 $cnt$,当某个状态的 $cnt=siz=1$ 时,更新答案.
int cur=1,l=0;
for (int i=1; i<=m; i++)
{
int c=t[i]-'a'+1;
while (fa(cur)&&!son(cur)[c]) cur=fa(cur),l=len(cur);
if (son(cur)[c]) cur=son(cur)[c],++l;
if (l>=len(fa(cur))+1) ++cnt[cur];
else ++cnt[fa(cur)];
}
int ans=oo;
for (int i=1; i<=tot; i++)
{
int u=dfn[i];
if (cnt[u]==1&&siz(u)==1) ans=min (ans,len(fa(u))+1);
cnt[fa(u)]+=cnt[u];
}
pr(ans==oo?-1:ans);
多个串建在同一个 $\text{SAM}$ 上.
每次插入时 $pre=1$ .
这会导致产生部分废点,不影响正确性,占空间.
不会.
给定 $\text{3}$ 个串,对于每个 $L \in [1, \min(len_A,len_B,len_C)]$ ,求满足 $A[a,a+L-1]=B[b,b+L-1]=C[c,c+L-1]$ 的三元组 $(a,b,c)$ 的数量.
建一个广义 $\text{SAM}$ ,求出对于每个点,分别在 $\text{3}$ 个串中的出现次数,然后相乘,更新该点最短长度的答案,再求一发前缀和即可.
for (int i=0; i< 3; i++)
{
int cur=1;
for (int j=1; j<=l[i]; j++)
cur=son(cur)[s[i][j]-'a'+1],++siz(cur)[i];
}
for (int i=1; i<=tot; i++)
++deg[fa(i)];
for (int i=1; i<=tot; i++)
if (!deg[i]) q.push (i);
while (q.size ())
{
int u=q.front (),f=fa(u); q.pop ();
for (int i=0; i< 3; i++)
siz(f)[i]+=siz(u)[i];
if (!(--deg[f])) q.push (f);
}
for (int i=1; i<=tot; i++)
{
int g=1LL*siz(i)[0]*siz(i)[1]*siz(i)[2]%mod;
(ans[len(fa(i))+1]+=g)%=mod,(ans[len(i)+1]-=g)%=mod;
}
for (int i=1; i<=mi; i++)
{
ans[i]+=ans[i-1];
ans[i]=(ans[i]%mod+mod)%mod;
pr(ans[i]);
}
临时插几句,后面用得到.
有两棵动态开点线段树,要将其对应位置的信息合并,递归进行合并.
二叉树.可以交换每个点的左右子树.要求前序遍历叶子的逆序对最少.
struct TREE { int ls,rs; LL sum; }t[N*20]; int tot=0;
#define ls(p) t[p].ls
#define rs(p) t[p].rs
#define sum(p) t[p].sum
int update (int p,int w,int l,int r)//单点增加
{
if (!p) p=++tot;
++sum(p);
if (l==r) return p;
int mid=(l+r)>>1;
if (w<=mid) ls(p)=update (ls(p),w,l,mid);
else rs(p)=update (rs(p),w,mid+1,r);
return p;
}
LL ans,an1,an2;
int merge (int x,int y)//合并,an1/2为x/y放在左边的逆序对数量
{
if (!x||!y) return x+y;
sum(x)+=sum(y);
an1+=sum(rs(x))*sum(ls(y));
an2+=sum(ls(x))*sum(rs(y));
ls(x)=merge (ls(x),ls(y));
rs(x)=merge (rs(x),rs(y));
return x;
}
int solve ()
{
int t,p=0; sc(t);
if (!t)
{
int ls,rs;
ls=solve (),rs=solve ();
an1=an2=0;
p=merge (ls,rs);
ans+=min (an1,an2);
}
else p=update (p,t,1,n);
return p;
}
用于维护每个点的 $endpos$ 或其它一些什么东西.
for (int i=1; i<=n; i++)
addchar (s[i]-'a'+1,i),rt[pre]=update (0,i,1,n);
for (int i=1; i<=totsam; i++)
++deg[fa(i)];
for (int i=1; i<=totsam; i++)
if (!deg[i]) q.push (i);
while (q.size ())
{
int u=q.front (),f=fa(u); q.pop ();
if (!f) continue;//!!!!!!
rt[f]=merge (rt[f],rt[u]);
if (!(--deg[f])) q.push (f);
}
给定一个串 $S$ 以及一个字符串数组 $T[1..m]$ , $q$ 次询问,每次问 $S$ 的子串 $S[p_l,p_r]$ 在 $T[wl,wr]$ 中的哪个串里的出现次数最多,并输出出现次数.
对 $T$ 建广义 $\text{SAM}$ ,对于每个点,求 $T$ 中的每个串(的子串)在该点中的出现次数,用线段树合并维护.
将查询离线(也有不离线的做法),按照 $wr$ 排序.
把 $S$ 放在 $\text{SAM}$ 上跑,对于每个以 $i$ 结尾的后缀,都可以得到在 $\text{SAM}$ 上的最大匹配长度 $len$ .
(同 $CF235C$ ) $len$ 可能比询问的子串长以至于不能得到最大答案,于是要向 $fa$ 跳,并保证满足 $(p_r-p_l+1)<=len(f)$ ,使用倍增加快向上跳的速度.
跳完后,在当前点的线段树上的 $[wl,wr]$ 查询最大值及位置.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdlib>
#include <queue>
#include <cmath>
#define pr(p) printf("%d\n",p)
const int oo=2139063143;
const int N=1010000;
const int mod=1000000007;
using namespace std;
typedef long long LL;
typedef double db;
inline void sc (int &x)
{
x=0; static int p; p=1; static char c; c=getchar();
while (!isdigit(c)) { if (c=='-') p=-1; c=getchar(); }
while ( isdigit(c)) { x=(x<<1)+(x<<3)+(c-48); c=getchar(); }
x*=p;
}
struct TREE { int ma,ls,rs,we,sum; }tree[N*20]; int totree=0,rt[N];
#define ma(p) tree[p].ma
#define ls(p) tree[p].ls
#define rs(p) tree[p].rs
#define we(p) tree[p].we
#define sum(p) tree[p].sum
int update (int p,int w,int d,int l,int r)
{
if (!p) p=++totree;
if (l==r) { ma(p)+=d,we(p)=l,sum(p)+=d; return p; }
int mid=(l+r)>>1;
if (w<=mid) ls(p)=update (ls(p),w,d,l,mid);
else rs(p)=update (rs(p),w,d,mid+1,r);
ma(p)=max (ma(ls(p)),ma(rs(p)));
we(p)=(ma(p)==ma(ls(p)))?we(ls(p)):we(rs(p));
sum(p)=sum(ls(p))+sum(rs(p));
return p;
}
int merge (int x,int y,int l,int r)
{
if (!x||!y) return x+y;
int z=++totree,mid=(l+r)>>1;
ls(z)=merge (ls(x),ls(y),l,mid);
rs(z)=merge (rs(x),rs(y),mid+1,r);
if (l==r) { ma(z)=ma(x)+ma(y),we(z)=l,sum(z)=ma(z); }
else
{
ma(z)=max (ma(ls(z)),ma(rs(z)));
we(z)=(ma(z)==ma(ls(z)))?we(ls(z)):we(rs(z));
sum(z)=sum(ls(z))+sum(rs(z));
}
return z;
}
#define fi first
#define se second
pair <int,int> query (int p,int l,int r,int L,int R)
{
if (l<=L&&R<=r) return make_pair (ma(p),we(p));
int mid=(L+R)>>1; pair <int,int> ans,lin; ans.fi=ans.se=0;
if (l<=mid) ans=query (ls(p),l,r,L,mid);
if (r> mid) lin=query (rs(p),l,r,mid+1,R);
if (lin.fi> ans.fi) ans=lin;
return ans;
}
struct SAM { int len,fa,son[28],siz; }sam[N<<1]; int totsam=1;
#define len(p) sam[p].len
#define fa(p) sam[p].fa
#define son(p) sam[p].son
#define siz(p) sam[p].siz
int pre=1;
void addchar (int c)
{
int f=pre,p=++totsam; pre=p;
len(p)=len(f)+1,siz(p)=1;
while (f&&!son(f)[c]) son(f)[c]=p,f=fa(f);
if (!f) { fa(p)=1; return ; }
int x=son(f)[c];
if (len(x)==len(f)+1) { fa(p)=x; return ; }
int y=++totsam;
len(y)=len(f)+1,fa(y)=fa(x),fa(x)=fa(p)=y;
memcpy (son(y),son(x),sizeof (son(y)));
while (f&&son(f)[c]==x) son(f)[c]=y,f=fa(f);
}
char s[N],sb[N];
vector <char> t[N];
int len[N];
queue <int> q;
int deg[N],dfn[N],fa[N][20];
struct LY { int wl,wr,pl,pr,id; }p[N];
bool ly (LY A,LY B) { return A.pr< B.pr; }
pair <int,int> ans[N];
int main()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
scanf ("%s",s+1);
len[0]=strlen (s+1);
int n; sc(n);
for (int i=1; i<=n; i++)
{
scanf ("%s",sb+1);
len[i]=strlen (sb+1);
pre=1;
for (int j=1; j<=len[i]; j++)
addchar (sb[j]-'a'+1),t[i].push_back (sb[j]);
}
int m; sc(m);
for (int i=1; i<=m; i++)
sc(p[i].wl),sc(p[i].wr),sc(p[i].pl),sc(p[i].pr),p[i].id=i;
for (int i=1; i<=n; i++)
for (int j=0,cur=1; j< len[i]; j++)
{
cur=son(cur)[t[i][j]-'a'+1];
rt[cur]=update (rt[cur],i,1,1,n);
}
for (int i=1; i<=totsam; i++)
++deg[fa(i)];
for (int i=1; i<=totsam; i++)
if (!deg[i]) q.push (i);
while (q.size ())
{
int u=q.front (),f=fa(u); q.pop (); dfn[++dfn[0]]=u;
if (!f) continue;
fa[u][0]=f;
rt[f]=merge (rt[f],rt[u],1,n);
if (!(--deg[f])) q.push (f);
}
for (int i=totsam; i>=1; i--)
{
int u=dfn[i];
for (int j=1; j<=18; j++)
fa[u][j]=fa[fa[u][j-1]][j-1];
}
sort (p+1,p+m+1,ly);
int cur=1,cnt=0,ask=1;
for (int i=1; i<=len[0]; i++)
{
int c=s[i]-'a'+1;
while (fa(cur)&&!son(cur)[c]) cur=fa(cur),cnt=len(cur);
if (son(cur)[c]) cur=son(cur)[c],++cnt;
while (p[ask].pr==i)
{
int l=p[ask].pr-p[ask].pl+1,sb=p[ask].id;
if (cnt< l) { ans[sb].se=p[ask].wl,++ask; continue; }
int u=cur;
for (int j=18; j>=0; j--)
{
int f=fa[u][j];
if (l<=len(f)) u=f;
}
ans[sb]=query (rt[u],p[ask].wl,p[ask].wr,1,n);
if (ans[sb].fi==0) ans[sb].se=p[ask].wl;
++ask;
}
}
for (int i=1; i<=m; i++)
printf ("%d %d\n",ans[i].se,ans[i].fi);
return 0;
}
给定字符串 $S$,定义一个字符串序列 $a[1…k]$ ,求最大的 $k$ ,满足性质: $a[i]$ 在 $a[i-1] (i>=2)$ 中出现至少两次(位置可重叠).
首先对 $S$ 建 $\text{SAM}$ ,线段树合并求 $endpos$ 集合.
设 $F[u]$ 为从根节点到 $u$ 点的最大的 $k$ 是多少.
由于每个点只从其 $fa$ 贡献而来,而 $fa$ 可能无法对该点进行贡献,于是还要记录 $top[u]$ 表示从根节点到 $u$ 点的路径上的最后一个有贡献的点(即 $top[u]$ 以下到 $fa$ 都没有贡献).
int ans=1;
for (int i=totsam-1; i>=1; i--)
{
int u=dfn[i],f=fa(u);
if (f==1) { F[u]=1,top[u]=u; continue; }
bool g=query (rt[top[f]],pos(u)-(len(u)-len(top[f])),pos(u)-1,1,n);
if (g) F[u]=F[f]+1,top[u]=u;
else F[u]=F[f],top[u]=top[f];
ans=max (ans,F[u]);
}
给定一个串 $S$ , $n$ 个询问,每个询问给出一个串 $T$ ,一个区间 $[l,r]$ 对应 $S$ 的一个子串 $G$,求 $T$ 的满足没有在 $G$ 中出现过的本质不同子串数量.
首先转化成求在 $G$ 中出现过的(不合法).
考虑只有 $l=1,r=| S |$ 的询问,对 $S$ 建 $\text{SAM}$ 将 $T$ 放在上面跑,此时 $T$ 的每个前缀对答案的贡献是前缀长度-匹配长度.
去重就再对 $T$ 建立 $\text{SAM}$ 即可.
再考虑 $l,r$ 任意的情况,先对 $S$ 建 $\text{SAM}+$ 线段树合并求 $endpos$ 集合.
于是 $T$ 在 $S.\text{SAM}$ 上跑的时候,判断是否可以前往 $son[c]$ 的标准不再仅仅是存在 $son[c]$ ,更需要 $son[c]$ 的 $endpos$ 集合中存在 $[l+cnt,r]$ 中任意一个位置( $cnt$ 为已匹配长度).
再记录 $T$ 的每个前缀能够匹配的长度 $lim$ ,剩下的和普通匹配差不多.
最后统计答案,对于每个在 $T.\text{SAM}$ 上的点,统计答案即可.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdlib>
#include <queue>
#include <cmath>
#define pr(p) printf("%lld\n",p)
const int oo=2139063143;
const int N=1010000;
const int mod=1000000007;
using namespace std;
typedef long long LL;
typedef double db;
inline void sc (int &x)
{
x=0; static int p; p=1; static char c; c=getchar();
while (!isdigit(c)) { if (c=='-') p=-1; c=getchar(); }
while ( isdigit(c)) { x=(x<<1)+(x<<3)+(c-48); c=getchar(); }
x*=p;
}
struct TREE { int ls,rs; }tree[N*20]; int totree=0,rt[N];
#define ls(p) tree[p].ls
#define rs(p) tree[p].rs
int update (int p,int w,int l,int r)
{
if (!w) return 0;
if (!p) p=++totree;
if (l==r) return p;
int mid=(l+r)>>1;
if (w<=mid) ls(p)=update (ls(p),w,l,mid);
else rs(p)=update (rs(p),w,mid+1,r);
return p;
}
int merge (int x,int y)
{
if (!x||!y) return x+y;
int z=++totree;
ls(z)=merge (ls(x),ls(y));
rs(z)=merge (rs(x),rs(y));
return z;
}
bool query (int p,int l,int r,int L,int R)
{
if (!p) return 0;
if (l<=L&&R<=r) return 1;
int mid=(L+R)>>1;
if (l<=mid&&query (ls(p),l,r,L,mid)) return 1;
if (r> mid&&query (rs(p),l,r,mid+1,R)) return 1;
return 0;
}
int n,m;
struct SAM { int len,fa,son[28],pos; }sam[N<<1]; int totsam=1;
#define len(p) sam[p].len
#define fa(p) sam[p].fa
#define son(p) sam[p].son
#define pos(p) sam[p].pos
int pre=1;
void addchar (int c,int pos)
{
int f=pre,p=++totsam; pre=p;
len(p)=len(f)+1,pos(p)=pos;
while (f&&!son(f)[c]) son(f)[c]=p,f=fa(f);
if (!f) { fa(p)=1; return ; }
int x=son(f)[c];
if (len(x)==len(f)+1) { fa(p)=x; return ; }
int y=++totsam; pos(y)=pos(x);
len(y)=len(f)+1,fa(y)=fa(x),fa(x)=fa(p)=y;
memcpy (son(y),son(x),sizeof (son(y)));
while (f&&son(f)[c]==x) son(f)[c]=y,f=fa(f);
}
struct sbSAM { int len,fa,son[28],sbsiz,sbpos; }sbbb[N<<1]; int sbtotsam;
#define sblen(p) sbbb[p].len
#define sbfa(p) sbbb[p].fa
#define sbson(p) sbbb[p].son
#define sbsiz(p) sbbb[p].sbsiz
#define sbpos(p) sbbb[p].sbpos
int sbpre;
void sbaddchar (int c,int pos)
{
int f=sbpre,p=++sbtotsam; sbpre=p;
sblen(p)=sblen(f)+1,sbsiz(p)=1,sbpos(p)=pos;
while (f&&!sbson(f)[c]) sbson(f)[c]=p,f=sbfa(f);
if (!f) { sbfa(p)=1; return ; }
int x=sbson(f)[c];
if (sblen(x)==sblen(f)+1) { sbfa(p)=x; return ; }
int y=++sbtotsam; sbpos(y)=sbpos(x);
sblen(y)=sblen(f)+1,sbfa(y)=sbfa(x),sbfa(x)=sbfa(p)=y;
memcpy (sbson(y),sbson(x),sizeof (sbson(y)));
while (f&&sbson(f)[c]==x) sbson(f)[c]=y,f=sbfa(f);
}
int lim[N];
void clean ()
{
for (int i=1; i<=sbtotsam; i++)
{
sblen(i)=sbfa(i)=sbsiz(i)=0;
memset (sbson(i),0,sizeof (sbson(i)));
lim[i]=0;
}
sbtotsam=sbpre=1;
}
char s[N],t[N];
queue <int> q;
int deg[N];
void getbzbt (int len)
{
sbtotsam=sbpre=1;
for (int i=1; i<=len; i++)
sbaddchar (t[i]-'a'+1,i);
}
int main()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
scanf ("%s",s+1);
n=strlen (s+1);
for (int i=1; i<=n; i++)
addchar (s[i]-'a'+1,i),rt[pre]=update (0,i,1,n);
for (int i=1; i<=totsam; i++)
++deg[fa(i)];
for (int i=1; i<=totsam; i++)
if (!deg[i]) q.push (i);
while (q.size ())
{
int u=q.front (),f=fa(u); q.pop ();
if (!f) continue;//!!!!!!
rt[f]=merge (rt[f],rt[u]);
if (!(--deg[f])) q.push (f);
}
sc(m);
while (m--)
{
scanf ("%s",t+1);
int len=strlen (t+1),l,r;
getbzbt (len);
sc(l),sc(r);
int cur=1,cnt=0;
for (int i=1; i<=len; i++)
{
int c=t[i]-'a'+1;
while (1)
{
int sb=query (rt[son(cur)[c]],l+cnt,r,1,n);
if (!sb)
{
if (!cnt) break;
--cnt;
if (cnt==len(fa(cur))) cur=fa(cur);
}
else { cur=son(cur)[c],++cnt; break; }
}
lim[i]=cnt;
}
LL ans=0;
for (int i=2; i<=sbtotsam; i++)
ans+=max (0,sblen(i)-max (sblen(sbfa(i)),lim[sbpos(i)]));
pr(ans);
clean ();
}
return 0;
}
CC 原创文章采用CC BY-NC-SA 4.0协议进行许可,转载请注明:“转载自:SAM”