ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2766. 后缀自动机

2022-05-02 23:32:50  阅读:123  来源: 互联网

标签:node 子串 状态 后缀 2766 int 自动机 节点


题目链接

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;
}

标签:node,子串,状态,后缀,2766,int,自动机,节点
来源: https://www.cnblogs.com/zyyun/p/16217333.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有