卧薪尝胆,厚积薄发。
BJWC2010 外星联络
Date: Wed Oct 10 21:48:15 CST 2018 In Category: NoCategory

Description:

统计一个 $01$ 串所有出现次数 $\geqslant 1$ 的子串的出现次数,按子串字典序排列。
$1\leqslant n\leqslant 3000$

Solution:

先建一个后缀自动机,然后套路统计一个 $Right$ 集合大小,由于 $SAM$ 从根出发的每条路径对应一个子串,按字典序 $DFS$ 整个后缀自动机,把沿途经过的点的 $Right$ 集合大小输出就行了。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
inline int read()
{
register char c = getchar();
while(!isdigit(c))c = getchar();
return c - '0';
}
int n;
#define MAXN 3010
struct node
{
int tr[2];
int par,maxl;
}s[MAXN << 1];
int ptr;
int newnode(int k){++ptr;s[ptr].maxl = k;return ptr;}
int root;
int last;
int siz[MAXN << 1];
void extend(int k)
{
int p = last;
int np = newnode(s[p].maxl + 1);siz[np] = 1;
for(;p && s[p].tr[k] == 0;p = s[p].par)s[p].tr[k] = np;
if(p == 0)s[np].par = root;
else
{
int q = s[p].tr[k];
if(s[p].maxl + 1 == s[q].maxl)s[np].par = q;
else
{
int nq = newnode(s[p].maxl + 1);
memcpy(s[nq].tr,s[q].tr,sizeof(s[q].tr));
s[nq].par = s[q].par;
s[q].par = s[np].par = nq;
for(;p && s[p].tr[k] == q;p = s[p].par)s[p].tr[k] = nq;
}
}
last = np;
return;
}
int c[MAXN],p[MAXN << 1];
void dfs(int k)
{
if(k != 1 && siz[k] != 1)printf("%d\n",siz[k]);
if(s[k].tr[0] != 0)dfs(s[k].tr[0]);
if(s[k].tr[1] != 0)dfs(s[k].tr[1]);
return;
}
int main()
{
scanf("%d",&n);
ptr = root = last = 1;
for(int i = 1;i <= n;++i)extend(read());
for(int i = 1;i <= ptr;++i)++c[s[i].maxl];
for(int i = 1;i <= n;++i)c[i] += c[i - 1];
for(int i = ptr;i >= 1;--i)p[c[s[i].maxl]--] = i;
for(int i = ptr;i >= 1;--i)siz[s[p[i]].par] += siz[p[i]];
dfs(root);
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡