bzoj 4516
考察后缀自动机的性质:每个节点上维护的子串数量等于自己与父节点的$len$之差
据此,我们在建后缀自动机的时候,对于每个插入的节点加上他与父节点的$len$之差即可
什么?分裂出来的节点怎么办?无所谓!不累计!
因为假设我们有一个点$p$,其父亲为$f$,分裂出的节点为$q$,那么我们知道,进行这次分裂之前,点$p$的贡献为$len_{p}-len_{f}$
而进行这次分裂之后,点$p$的贡献为$len_{p}-len_{q}$,点$q$的贡献为$len_{q}-len_{f}$,因此分裂后的总贡献仍为$len_{p}-len_{f}$,这个值不变,因此分裂操作不影响答案
建后缀自动机的时候用map存出边即可
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> #include <map> #define ll long long using namespace std; struct SAM { map <int,int> M; int len,pre; }s[200005]; int n; ll ans=0; int tot=1,las=1; void ins(int c) { int nwp=++tot; s[nwp].len=s[las].len+1; int lsp; for(lsp=las;lsp&&s[lsp].M.find(c)==s[lsp].M.end();lsp=s[lsp].pre)s[lsp].M[c]=nwp; if(!lsp)s[nwp].pre=1; else { int lsq=s[lsp].M[c]; if(s[lsq].len==s[lsp].len+1)s[nwp].pre=lsq; else { int nwq=++tot; s[nwq]=s[lsq]; s[nwq].len=s[lsp].len+1; s[lsq].pre=s[nwp].pre=nwq; while(s[lsp].M[c]==lsq)s[lsp].M[c]=nwq,lsp=s[lsp].pre; } } ans+=(ll)(s[nwp].len-s[s[nwp].pre].len),las=nwp; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { int x; scanf("%d",&x); ins(x); printf("%lld\n",ans); } return 0; }