后缀自动机+后缀树
有一种不难想到的后缀数组的做法,详见黄学长题解
然而我做这题的目的主要是熟悉一下后缀自动机和后缀树- -
一个有趣的性质:反串的后缀自动机的parent树就是正串的后缀树,其树上的边权长度=parent树上儿子的len-父亲的len
构出后缀树之后就是一个小DP啦。
#include<cstdio>
#include<cstring>
#define N 500010
#define S 28
using namespace std;
namespace ziqian
{
typedef long long ll;
char s[N];
ll ans = 0;
int last[N*2], ecnt, siz[N*2]; struct edge{int next, to;}e[N<<1];
void addedge(int a, int b){e[++ecnt] = (edge){last[a], b}; last[a] = ecnt;}
struct SAM
{
SAM *par, *ch[S];
int len;
}mem[N*2], *tot, *null, *tai, *root;
SAM *newSAM(int l)
{
SAM *r = ++tot;
*r = *null;
r->len = l;
return r;
}
void init_SAM()
{
tot = mem;
null = ++tot;
null -> par = null;
for(int i = 0; i < S; i++) null->ch[i] = null;
null->len = 0;
root = newSAM(0);
tai = root;
}
void extend(int v)
{
SAM *p = tai, *np = newSAM(p->len + 1); tai = np; siz[np - mem] = 1;
for(; p != null && p->ch[v] == null; p = p->par) p->ch[v] = np;
if(p == null) np -> par = root;
else
{
SAM *q = p->ch[v];
if(p->len + 1 == q->len) np->par = q;
else
{
SAM *nq = newSAM(p->len + 1);
*nq = *q;
nq -> len = p->len + 1;
q->par = np->par = nq;
for(; p != null && p->ch[v] == q; p = p->par) p->ch[v] = nq;
}
}
}
void dp(int x)
{
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to;
dp(y);
ans -= 2ll * siz[x] * siz[y] * (mem+x)->len;
siz[x] += siz[y];
}
}
void main()
{
scanf("%s",s+1);
int n = strlen(s+1);
init_SAM();
for(int i = n; i; i--) extend(s[i] - 'a');
for(int i = 1; i <= n; i++) ans += (ll) i * (n-1);
for(SAM *cur = tot; cur != mem; cur--)
addedge(cur->par - mem, cur - mem);
dp(root - mem);
printf("%lld\n",ans);
}
}
int main()
{
ziqian::main();
}