• Welcome to the world's largest Chinese hacker forum

    Welcome to the world's largest Chinese hacker forum, our forum registration is open! You can now register for technical communication with us, this is a free and open to the world of the BBS, we founded the purpose for the study of network security, please don't release business of black/grey, or on the BBS posts, to seek help hacker if violations, we will permanently frozen your IP and account, thank you for your cooperation. Hacker attack and defense cracking or network Security

    business please click here: Creation Security  From CNHACKTEAM

Recommended Posts

题目链接

2766. 后缀自动机

给定一个长度为\(n\)且只包含小写字母的字符串\(S\)。

对于\(S\)的出现次数不为\(1\)的所有子串,设其\(value\)为子串的出现次数\ ( \)子串的长度。

请计算一下,\(value\)的最大值是多少。

输入格式

总共一行,包含一串\(n\)小写字母。

输出格式

一共一行,输出一个整数,表示答案。

数据范围

\(1n10^6,\)

请确保至少有一个出现次数大于\(1\)的子字符串。

输入样例:

输出样例:

解题思路

后缀自动机

后缀自动机是一种能够精确存储一个字符串的所有子串的数据结构,它包含不超过\(2n\)个节点和不超过\(3n\)条边,其中关键在于\(endpos(s)\,它表示子串\(s\)的最后一个字符出现的所有位置,后缀(endpos(s)\)的子串,即\(endpos\)的等价类,是最关键的性质:后缀自动机的每个节点状态包含一个连续子串,即其他字符串是连续的

此外,还有两个关键方面:

image

后缀自动机有一个源点(空字符串)和一个汇点(所有字符到这个点形成的后缀),其中蓝色边代表一个有向无环图,绿色边代表一棵树。蓝边的含义类似于字典树,即通过旧状态连接字母的边形成新状态,绿边的含义是当前节点中最短子串去掉第一个字母后某个状态的最长子串。所有绿色边连接的所有节点形成的状态是连续的,即某个状态代表一个子串,它的连通状态代表前一个状态的首字母的另一个连续子串,即连通状态中的子串长度是递减的。

构造过程:以增量方式使用,即逐个插入字符,后缀自动机中维护三条信息:绿边的父节点、其最长子串的长度、其他用字母连接的状态节点。每增加一个新的字母,就设置一个新的状态,其最长长度为前一个状态的最长长度加一(因为增加了一个新的字母,旧的状态转移到新的状态)。同时,对于前一个状态的绿色边所连接的节点,由于前一个节点每次都有变化,增加了新的字母,这些节点被连接到新的状态节点,这意味着新的状态节点包含了到达该节点的所有子串。如果新状态节点由绿色边连接,则新状态节点将连接到新状态节点。

时间复杂度:\(O(n)\)

代码

//problem3360后缀自动机

//Contest: AcWing

//URL : https://www.acwing.com/problem/content/description/2768/

//内存限制: 512 MB

//时间限制: 1000毫秒

//

//由CP编辑器(https://cpeditor.org)提供支持

//%%%Skyqwq

#包含位/标准数据。h

//#定义int long long

# define help { CIN . tie(NULL);cout.tie(空);}

#定义按钮推回

#首先定义fi

#定义se秒

#定义mkp制造_配对

使用命名空间std

typedef long long LL

typedef pairint,int PII;

typedef pairLL,LL PLL

template typename T bool chkMax(T x,T y) { return (y x)?x=y,1 : 0;}

模板类型名T bool chkMin(T x,T y) { return (y x)?x=y,1 : 0;}

模板类型名T void inline read(T x) {

int f=1;x=0;char s=getchar();

while(s ' 0 ' | | s ' 9 '){ if(s=='-')f=-1;s=getchar();}

while (s='9' s='0') x=x * 10 (s ^ 48),s=getchar();

x *=f;

}

const int N=2e 6 5;

int h[N],ne[N],e[N],idx,lst=1,CNT=1;

char s[N];

LL f[N],res

结构节点

{

int fa,len

int ch[26];

}节点[N];

void add(int a,int b)

{

e[idx]=b,ne[idx]=h[a],h[a]=idx;

}

无效延伸(中间)

{

int p=lst,np=lst=cnt

节点[np]。len=node[p]。镜头1;

f[NP]=1;

for(;p!节点[p]。ch[c];p=节点[p]。fa)节点[p]。ch[c]=NP;

如果(!p)节点[np]。fa=1;

其他

{

int q=node[p].ch[c];

如果(节点问.len==node[p].镜头1)节点[名词]名词短语.fa=q;

其他

{

int nq=cnt

节点[NQ]=节点[q];

节点[nq].len=node[p].镜头1;

节点[名词]名词短语.fa=节点问.fa=nq

for(;节点,节点.ch[c]==q;p=节点[p]2 .fa)节点[p]2 .ch[c]=NQ;

}

}

}

void dfs(int u)

{

for(int I=h;~我;我=ne)

{

DFS(e);

f=f[e];

}

if(f1)res=max(res,f*node.len);

}

int main()

{

scanf('%s ',s);

for(int I=0;s;我)扩展(s-' a ');

memset(h,-1,sizeof h);

for(int I=2;i=cntI)添加(节点【我】。法,我);

外勤支助部(1);

printf('%lld ',RES);

返回0;

}

Link to comment
Share on other sites