P3804 【模板】后缀自动机(SAM)
题目描述
给定一个只包含小写字母的字符串 \(S\)。
请你求出 \(S\) 的所有出现次数不为 \(1\) 的子串的出现次数乘上该子串长度的最大值。
输入格式
一行一个仅包含小写字母的字符串 \(S\)。
输出格式
一个整数,为所求答案。
输入输出样例 #1
输入 #1
abab
输出 #1
4
说明/提示
对于 \(10 \%\) 的数据,\(\lvert S \rvert \le 1000\)。
对于 $100% $的数据,\(1 \le \lvert S \rvert \le {10}^6\)。
- 2023.7.30:添加一组 hack 数据。
题解
#include <bits/stdc++.h>
using namespace std;
const int N=2e6+10;
typedef long long ll;
int n;
char s[N];
vector<int> e[N];//邻接表
ll cnt[N],ans;
int tot=1,np=1;
//fa链接边终点,ch转移边终点,len最长串长度
int fa[N],ch[N][26],len[N];
void extend(int c)
{
//p回跳指针, np新点, q链接点, nq新链接点
int p=np; np=++tot;//p指向旧点, np是新点
len[np]=len[p]+1; cnt[np]=1;//子串出现次数
//p沿链接边回跳,从旧点向新点建转移边
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
//1.如果c是新字符,从新点向根节点建链接边
if(p==0)fa[np]=1;
else
{//如果c是旧字符
int q=ch[p][c];//q是链接点
//2.若链接点合法,从新点向q建链接边
if(len[q]==len[p]+1)fa[np]=q;
//3.若链接点不合法,则裂开q点,重建两类边
else
{
int nq=++tot;//nq是新链接点
len[nq]=len[p]+1;
//重建nq,q,np的链接边
fa[nq]=fa[q]; fa[q]=nq; fa[np]=nq;
//指向q的转移边改为指向nq
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
//从q发出的转移边复制给nq
memcpy(ch[nq],ch[q],sizeof(ch[q]));
}
}
}
void dfs(int u)
{
for(auto v : e[u])
{
dfs(v);
cnt[u]+=cnt[v];
}
if(cnt[u]>1)ans=max(ans,cnt[u]*len[u]);
}
void solve()
{
cin>>s;
for(int i=0;s[i];i++)extend(s[i]-'a');
for(int i=2;i<=tot;i++)e[fa[i]].push_back(i);
dfs(1);
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
// cin>>t;
while(t--)
{
solve();
}
return 0;
}

浙公网安备 33010602011771号