luoguP3804 后缀自动机
这个题转化一下就是求每个子串的出现次数,也就是endpos集合的大小。
参考oiwiki中的应用——出现次数
就是新建节点时初始化cnt为1,clone时初始化cnt为0.
然后长度排序,在link树中从叶子更新到根节点,就统计完了。
这里主要解释一下为什么clone的点是赋值为0,非clone点赋值为1.
首先,非clone点可以肯定它第一次出现,所以把它赋值为1,这个应该可以理解,那么clone点为什么就初始化为0?
因为对于一个点,分裂的前提是len[p]+1 != len[q]
也就是说,一个点分裂成了两个endpos集合。在一棵link树中,一个节点的endpos集合被它的儿子平分了,所以被clone的点,它应该由它的子树更新而来,它本身不需要初始化,因为初始化之后,结果会变大。
实际上,我们初始化为1的点就是插入s的顺序的那些点,也就是说,按照插入顺序,依次经过的每个点它在后缀链接树上是叶子节点。
这个性质挺有趣的。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<ctime>
#define up(i,x,y) for(int i = x;i <= y;i ++)
#define down(i,x,y) for(int i = x;i >= y;i --)
#define mem(a,b) memset((a),(b),sizeof(a))
#define mod(x) ((x)%MOD)
#define lson p<<1
#define rson p<<1|1
using namespace std;
typedef long long ll;
const int SIZE = 2000010;
const int INF = 2147483640;
const double eps = 1e-8;
inline void RD(int &x)
{
x = 0; char c; c = getchar();
bool flag = 0;
if(c == '-') flag = 1;
while(c < '0' || c > '9') {
if(