• bzoj 3676 [Apio2014]回文串(Manacher+SAM)


    【题目链接】

       

        http://www.lydsy.com/JudgeOnline/problem.php?id=3676

    【题意】

        给定一个字符串,定义一个串的权值为长度*出现次数,求最大权的回文子串。

    【思路】

        马拉车求出本质不同的回文子串。

        对于一个回文子串,在SAM中用倍增法在O(logn)的时间得到它的出现次数,即SAM中每个节点的right集大小,倍增数组和right都可以通过提前处理得到。

        更新答案即可。

     先来考虑一个简单的问题:

    给出一个串S(|S|<=1000000)和M个询问,每次询问S中[si,ti]这一段串在总串中出现过几次。
    显然我们可以建出后缀数组并用二分+ST表简单地完成。
    但如果我们一定要用后缀自动机的知识呢?
    我们会发现,倘若我们能快速找到一个节点(状态)表示当前s~t这一段,只需直接调用它的size即可。
    (有关size的预处理:在SAM建立好之后,用所有点的size去累加它的parent的size)
    那么如何快速找到这样一个状态呢?
    首先SAM有一个性质:把每一个节点向它的parent连边,得到的树是原串的逆序串的后缀树。(只是这棵后缀树压缩后的边权都不知道)
    也就是说,如果我们构建出一棵SAM,它将同时有后缀树和trie的性质。
    举个例子,比如字符串baabaaa。设询问为s=5,t=6
    首先画出对应的后缀树(空节点不再画出):
    注意此时后缀树中“浅”的点表示的是“后缀”。
    我们先跑出从1开始到t的状态s,此时设我们在SAM中的节点p。样例里p对应在后缀树的最下面那个点。
    但是我们发现1~t的状态太长了,我们只需要s~t的状态。
    这样我们可以在这棵后缀树上倍增,能往某个祖先跑就往某个祖先跑。能跑的依据就是该祖先的深度>=t-s+1
    这样,我们就跑到了p节点在后缀树上的父亲的父亲,然后直接在SAM里调用它的信息即可。
    这里还有一个细节问题:如果询问是s=3,t=6,应该返回哪个点呢?
    这个时候发现不能完全覆盖,要不一个点少一些,要不一个点多一些。
    显然要把剩下的部分也选进去,也就是说仍然在p这个点。合法性显然,且可以证明这样最优。
     
                                             Quote from Here

    【代码】

      1 #include<set>
      2 #include<cmath>
      3 #include<queue>
      4 #include<vector>
      5 #include<cstdio>
      6 #include<cstring>
      7 #include<iostream>
      8 #include<algorithm>
      9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
     10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
     11 #define rep(a,b,c) for(int a=(b);a>=(c);a--)
     12 using namespace std;
     13 
     14 typedef long long ll;
     15 const int N = 6e5+10;
     16 const int D = 21;
     17 
     18 char s[N];
     19 int n,p[N];
     20 
     21 struct SAM 
     22 {
     23     
     24     ll ans;
     25     int sz,last,ch[N][26],fa[N],R[N],pos[N],l[N],b[N],cnt[N],fat[N][D];
     26     SAM() 
     27     {
     28         sz=ans=0; last=++sz;
     29         memset(cnt,0,sizeof(cnt));
     30         memset(R,0,sizeof(R));
     31         memset(fat,0,sizeof(fat));
     32     }
     33     void add(int c,int id)
     34     {
     35         int np=++sz,p=last; last=np;
     36         l[np]=l[p]+1; R[np]=1; pos[id]=last;
     37         for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
     38         if(!p) fa[np]=1;
     39         else {
     40             int q=ch[p][c];
     41             if(l[q]==l[p]+1) fa[np]=q;
     42             else {
     43                 int nq=++sz; l[nq]=l[p]+1;
     44                 memcpy(ch[nq],ch[q],sizeof(ch[q]));
     45                 fa[nq]=fa[q];
     46                 fa[q]=fa[np]=nq;
     47                 for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; 
     48             }
     49         }
     50     }
     51     void get_pre()
     52     {
     53         FOR(i,1,sz) cnt[l[i]]++;
     54         FOR(i,1,n) cnt[i]+=cnt[i-1];
     55         rep(i,sz,1) b[cnt[l[i]]--]=i;
     56         rep(i,sz,1) R[fa[b[i]]]+=R[b[i]];
     57         
     58         FOR(i,1,sz) {
     59             fat[i][0]=fa[i];
     60             FOR(j,1,D-1)
     61                 fat[i][j]=fat[fat[i][j-1]][j-1];
     62         }
     63     }
     64     void get_ans(int u,int v)
     65     {
     66         int x=pos[v];
     67         for(int i=D-1;i>=0;i--) {
     68             int t=fat[x][i];
     69             if(l[t]>=v-u+1) x=t;
     70         }
     71         ans=max(ans,(ll)R[x]*(v-u+1));
     72     }
     73     
     74 } sam;
     75 
     76 void Manacher()
     77 {
     78     int mx=0,id;
     79     for(int i=1;i<=n;i++) {
     80         if(mx>i) p[i]=min(mx-i,p[2*id-i-1]);
     81         else p[i]=0;
     82         while(s[i+p[i]+1]==s[i-p[i]]) {
     83             p[i]++;
     84             sam.get_ans(i-p[i]+1,i+p[i]);
     85         }
     86         if(p[i]+i>mx) mx=p[i]+i,id=i;
     87     }
     88     mx=0;
     89     for(int i=1;i<=n;i++) {
     90         if(mx>i) p[i]=min(mx-i-1,p[2*id-i]);
     91         else p[i]=1,sam.get_ans(i,i);
     92         while(s[i+p[i]]==s[i-p[i]]) {
     93             p[i]++;
     94             sam.get_ans(i-p[i]+1,i+p[i]-1);
     95         }
     96         if(p[i]+i>mx) mx=p[i]+i,id=i;
     97     }
     98 }
     99 
    100 int main()
    101 {
    102     scanf("%s",s+1);
    103     n=strlen(s+1);
    104     FOR(i,1,n) sam.add(s[i]-'a',i);
    105     sam.get_pre();
    106     s[0]='+',s[n+1]='-';
    107     Manacher();
    108     printf("%lld",sam.ans);
    109     return 0;
    110 }
  • 相关阅读:
    【分布式锁】RedLock 实现分布式锁
    【反射】遍历对象属性名与值
    【ABP.Net】2.多数据库支持&&初始化数据库
    【ABP.Net】1.创建项目&介绍框架结构
    【Vue-Cli3.0】【2】渲染
    【nuget】PackageReference
    【Vue-Cli3.0】【1】创建一个Vue-Cli3.0的项目
    【干货】干货篇
    踩坑记录-Redis(Windows)的getshell
    极致CMS存储XSS|前台打后台COOKIE漏洞复现
  • 原文地址:https://www.cnblogs.com/lidaxin/p/5349404.html
Copyright © 2020-2023  润新知