微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

2766. 后缀自动机

题目链接

2766. 后缀自动机

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

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

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

输入格式

共一行,包含一个由 \(n\) 个小写字母构成的字符串。

输出格式

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

数据范围

\(1≤n≤10^6,\)
保证至少存在一个子串出现次数大于 \(1\)。

输入样例:

aabab

输出样例:

4

解题思路

后缀自动机(SAM)

后缀自动机是一种能够恰好将一个字符串的所有子串存储下来的数据结构,且其中含有的节点数量不超过 \(2n\) 个,含有的边数不超过 \(3n\) 条,其中关键在于 \(endpos(s)\),其表示子串 \(s\) 的尾字符出现的所有位置,而后缀自动机中的每一个节点状态表示所有等于 \(endpos(s)\) 的子串,即 \(endpos\) 的等价类,其中最为关键的性质:后缀自动机的每个节点状态包含一段连续的子串,即其他串都是最长串的连续后缀,即串的长度依次递减
另外,其关键还有两条边:

image


后缀自动机有一个源点(表示空串)和一个汇点(表示到这个点的所有字符形成的后缀),其中,蓝边表示的是一个有向无环图,绿边表示的是一棵树,蓝边的含义类似于字典树,即新状态是通过旧状态通过字母连边形成的,绿边的含义为当前节点中的最短子串去掉第一个字母后形成的某个状态的最长子串,可以看出,所有绿边连向的所有节点形成的状态都是连续的,即某一个状态表示一段的子串,则其连向的状态表示上一个状态首字母的又一连续子串,即连向的状态中的子串长度在递减

构造过程:采用增量的方式,即一个一个字符插入,后缀自动机中维护三个信息:绿边的父节点,其最长子串的长度,其通过字母连向的其他状态节点。每次有新字母加进来时,设置一个新状态,其最长长度为上一个状态最长长度加一(因为有新字母加入,旧状态向新状态转移),同时由于加入新字母,对于前一个状态绿边连向的节点,由于每次变化的都是前面的节点,有新字母加入,这些节点连向新状态节点,表示新的状态节点包含到达该节点的所有子串,如果通过绿边到了空串表示的位置,则当前节点能表示的最小子串为新加入的字母,则其绿边应该指向空串表示的位置,否则,

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

代码

// Problem: 后缀自动机
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/2768/
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename 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=2e6+5;
int h[N],ne[N],e[N],idx,lst=1,cnt=1;
char s[N];
LL f[N],res;
struct Node
{
	int fa,len;
	int ch[26];
}node[N];
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void extend(int c)
{
	int p=lst,np=lst=++cnt;
	node[np].len=node[p].len+1;
	f[np]=1;
	for(;p&&!node[p].ch[c];p=node[p].fa)node[p].ch[c]=np;
	if(!p)node[np].fa=1;
	else
	{
		int q=node[p].ch[c];
		if(node[q].len==node[p].len+1)node[np].fa=q;
		else
		{
			int nq=++cnt;
			node[nq]=node[q];
			node[nq].len=node[p].len+1;
			node[np].fa=node[q].fa=nq;
			for(;p&&node[p].ch[c]==q;p=node[p].fa)node[p].ch[c]=nq;
		}
	}
}
void dfs(int u)
{
	for(int i=h[u];~i;i=ne[i])
	{
		dfs(e[i]);
		f[u]+=f[e[i]];
	}
	if(f[u]>1)res=max(res,f[u]*node[u].len);
}
int main()
{
    scanf("%s",s);
    for(int i=0;s[i];i++)extend(s[i]-'a');
    memset(h,-1,sizeof h);
    for(int i=2;i<=cnt;i++)add(node[i].fa,i);
    dfs(1);
    printf("%lld",res);
    return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐