zoukankan      html  css  js  c++  java
  • 【SAM manacher 倍增】bzoj3676: [Apio2014]回文串

    做法一:PAM;做法二:SAM+manacher.前缀树上倍增

    Description

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

    Input

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

    Output

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

    【数据规模与评分】 

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


    题目分析

    考虑暴力怎么做:首先对字符串$S$建立SAM,再使用manacher对所有$O(n)$级别的本质不同的回文子串查询出现个数。由于每次查询是$O(n)$的,所以暴力的复杂度是$O(n^2)$.

    有一种SAM上计数的基本套路:在前缀树上倍增或是其他在树上的常规操作。对于这题则是考虑用倍增快速查询$S(l,r)$在全串中的出现次数。

    用$fa[i][j]$表示点$i$在前缀树上向上跳$2^j$步的父亲点数,那么从$p=id[r]$(即子串$S(1,r)$插入完毕的一部分SAM)开始查询,若$len[p]≥r-l+1$就说明点$p$所在的子树仍都是$S(l,r)$的后缀,因此一直向上跳直到不满足限制的点$p$的$size[p]$就是回文子串$S(l,r)$出现次数。

     1 #include<bits/stdc++.h>
     2 const int maxn = 600035;
     3 
     4 int n,g[maxn],lg2[maxn>>1];
     5 long long ans;
     6 int size[maxn],dep[maxn],id[maxn],sta[maxn];
     7 int cnt[maxn],pos[maxn];
     8 char s[maxn],t[maxn];
     9 struct SAM
    10 {
    11     int lst,tot;
    12     int fa[maxn][21],len[maxn];
    13     std::map<int, int> ch[maxn];          //似乎略卡空间
    14     SAM(){lst = tot = 1;}
    15     void extend(int c, int id)
    16     {
    17         int p = lst, np = ++tot;
    18         lst = np, len[np] = len[p]+1, sta[id] = np;                    //sta[np]=id
    19         for (; p&&!ch[p][c]; p=fa[p][0]) ch[p][c] = np;
    20         if (!p) fa[np][0] = 1;
    21         else{
    22             int q = ch[p][c];
    23             if (len[q]==len[p]+1) fa[np][0] = q;
    24             else{
    25                 int nq = ++tot;
    26                 len[nq] = len[p]+1, ch[nq] = ch[q];
    27                 fa[nq][0] = fa[q][0], fa[q][0] = fa[np][0] = nq;
    28                 for (; ch[p][c]==q; p=fa[p][0]) ch[p][c] = nq;
    29             }
    30         }
    31         size[np] = 1;
    32     }
    33     void build()
    34     {
    35         for (int i=1; i<=tot; i++) ++cnt[len[i]];
    36         for (int i=1; i<=tot; i++) cnt[i] += cnt[i-1];
    37         for (int i=tot; i>=1; i--) pos[cnt[len[i]]--] = i;
    38         for (int i=tot; i>=1; i--)
    39             size[fa[pos[i]][0]] += size[pos[i]];
    40         for (int i=1; i<=tot; i++)
    41         {
    42             int p = pos[i];
    43             dep[p] = dep[fa[p][0]]+1;
    44             for (int j=1; j<=20; j++)
    45                 fa[i][j] = fa[fa[i][j-1]][j-1]; 
    46         }
    47     }
    48     void match(int l, int r)
    49     {
    50         if (l > r||l < 1||r > n) return;
    51         int p = sta[r];
    52         for (int i=lg2[dep[p]]; i>=0; i--)
    53         {
    54             int fat = fa[p][i];
    55             if (len[fat] >= r-l+1) p = fat;
    56         }
    57         ans = std::max(ans, 1ll*size[p]*(r-l+1));
    58     }
    59 }f;
    60 
    61 void manacher()
    62 {
    63     int m = 0, mid = 1;
    64     t[0] = '!', t[++m] = '@';
    65     for (int i=1; i<=n; i++)
    66         t[++m] = s[i], id[m] = i, t[++m] = '@';
    67     for (int i=1, mx=-1; i<m; i++)
    68     {
    69         if (i >= mx) g[i] = 1;
    70         else g[i] = std::min(mx-i, g[2*mid-i]);
    71         f.match(id[i-g[i]+2], id[i+g[i]-2]);
    72         for (; t[i-g[i]]==t[i+g[i]]; )
    73             ++g[i], f.match(id[i-g[i]+2], id[i+g[i]-2]);        //id[i-g[i]], id[i+g[i]]
    74         if (i+g[i] > mx) mx = i+g[i], mid = i;
    75     }
    76 }
    77 int main()
    78 {
    79     scanf("%s",s+1);
    80     n = strlen(s+1);
    81     for (int i=1; i<=n; i++)
    82         f.extend(s[i]-'a', i), lg2[i] = i>1?(lg2[i>>1]+1):0;
    83     f.build();
    84     manacher();
    85     printf("%lld
    ",ans);
    86     return 0;
    87 }

    END

  • 相关阅读:
    python scrapy爬取前程无忧招聘信息
    scrf 原理及flask-wtf防护
    Django 惰性机制
    Django 中配置MySQL数据库
    Django的安装命令
    python装饰器
    python面向对象之继承
    OSI七层模型
    面向对象
    python函数
  • 原文地址:https://www.cnblogs.com/antiquality/p/10526075.html
Copyright © 2011-2022 走看看