[Bzoj3676][Apio2014]回文串(后缀自动机)(parent树)(倍增)

本文提供了一种使用后缀自动机和Manacher算法解决Apio2014回文串问题的方法,通过构造后缀自动机parent树并结合Manacher算法求得所有回文子串中的最大出现值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3676: [Apio2014]回文串


 

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 3396  Solved: 1568
[Submit][Status][Discuss]

Description


 

考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出 
现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最 
大出现值。 

Input


 

输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。 

Output


 


输出一个整数,为逝查回文子串的最大出现值。 

Sample Input


 

【样例输入l】
abacaba 

 


【样例输入2]
www 

 


Sample Output


 

【样例输出l】
7 

 


【样例输出2]
4 

 


HINT


 

 



一个串是回文的,当且仅当它从左到右读和从右到左读完全一样。 

在第一个样例中,回文子串有7个:a,b,c,aba,aca,bacab,abacaba,其中: 

● a出现4次,其出现值为4:1:1=4 

● b出现2次,其出现值为2:1:1=2 

● c出现1次,其出现值为l:1:l=l 

● aba出现2次,其出现值为2:1:3=6 

● aca出现1次,其出现值为1=1:3=3 

●bacab出现1次,其出现值为1:1:5=5 

● abacaba出现1次,其出现值为1:1:7=7 

故最大回文子串出现值为7。 

【数据规模与评分】 

数据满足1≤字符串长度≤300000。

 

分析:


回文树眼题,但是我不会回文树QAQ

 

于是后缀自动机加manacher,构造出后缀自动机parent树后倍增每个点2^i能到达的点的dis值。
然后就可以做了。。。

AC代码:


 

# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
const int N = 3e5 + 12;
long long ans;
int ch[N << 1][26];
int dis[N << 1],fa[N << 1],w[N << 1],cnt = 1,len;
int que[N << 1],p[N << 1],dt,Log[19],g[N << 1][19],Id[N];
char str[N],s[N << 1];
int Sam(int c,int last)
{
    int u = last,cur;
    while(u && !ch[u][c])que[++que[0]] = u,u = fa[u];
    if(!u)
    {
        cur = ++cnt;fa[cur] = 1;
        while(que[0])ch[que[que[0]--]][c] = cur;
    }
    else 
    {
        int v = ch[u][c];
        if(dis[v] == dis[u] + 1)
        {
            cur = ++cnt;fa[cur] = v;
            while(que[0])ch[que[que[0]--]][c] = cur;
        }
        else
        {
            int av = ++cnt;dis[av] = dis[u] + 1;cur = ++cnt;
            while(que[0])ch[que[que[0]--]][c] = cur;
            memcpy(ch[av],ch[v],sizeof ch[v]);
            fa[av] = fa[v];fa[v] = fa[cur] = av;
            while(u && ch[u][c] == v)ch[u][c] = av,u = fa[u]; 
        }
    }
    dis[cur] = dis[last] + 1;
    return cur;
}
void Manacher()
{
  s[0] = '$';s[dt = 1] = '#';
  for(int i = 1;i <= len;i++)s[++dt] = str[i],s[++dt] = '#';
  int mx = 2,id = 1;p[0] = p[1] = 0;
  for(int i = 2;i < dt;i++)
  {
      p[i] = min(p[2 * id - i],mx - i);
      while(s[i + p[i] + 1] == s[i - p[i] - 1])
    {
     p[i]++;
     if((i + p[i]) & 1)
     {
         int now = Id[(i + p[i] - 1) >> 1],L = ((i + p[i] - 1) >> 1) - ((i - p[i] + 1) >> 1) + 1;
         for(int j = 18;~j;j--)if(dis[g[now][j]] >= L)now = g[now][j];
         ans = max(ans,1LL * L * w[now]);
     }
    }
      if(i + p[i] > mx)
      {
          id = i;
          mx = i + p[i];
      }
  }
}
void init()
{
    Log[0] = 1;for(int i = 1;i < 19;i++)Log[i] = Log[i - 1] << 1;
    for(int i = 1;i <= cnt;i++)g[i][0] = fa[i];//这里考场上写成了<=len,结果得了90
    for(int j = 1;j <= 18;j++)
     for(int i = 1;i <= cnt;i++)
     g[i][j] = g[g[i][j - 1]][j - 1];
}
int main()
{
    scanf("%s",str + 1);len = strlen(str + 1);Id[0] = 1;
    for(int i = 1;i <= len;i++)Id[i] = Sam(str[i] - 'a',Id[i - 1]),w[Id[i]]++;
    for(int i = 1;i <= cnt;i++)p[dis[i]]++;
    for(int i = 1;i <= len;i++)p[i] += p[i - 1];
    for(int i = cnt;i >= 1;i--)que[p[dis[i]]--] = i;
    for(int i = cnt;i >= 1;i--)w[fa[que[i]]] += w[que[i]];
    init();Manacher();
    printf("%lld\n",ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/lzdhydzzh/p/8807722.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值