zoukankan      html  css  js  c++  java
  • 「专题总结」回文自动机PAM

    为了备课,把做完的专题的总结咕了这么久。。。

    主要是自己做题做的太慢了,所以讲SAM的时候准备也不充分。

    在把讲课时间不断咕之后依然是粗制滥造,锅很多,所以效果很差。而且还有人没听懂。。。

    一半人都做了5道题以上了,另一半人还没怎么看,基本所有人都有预习。

    得不到任何反馈,也不知道速度如何。就当凑活吧。

    挺失败的。可能也没有下一次机会了。

    我也不知道后缀数组推荐率是怎么达到100%的。。。那次我讲的自己也很满意

    一到难的知识点我就不行了嘛。。。主要是自己理解也很不深刻

    至少也还是在3个多小时之内把纯手打的2万个字全都口胡完了。应该不算耽误大家太多时间。

    最近状态很差嘛。。。要提高效率了。

    扯远了。

    回文自动机的思路在于往两边同时加上相同字符。是一种可以在线的数据结构。

    len维护回文串的长度,fail指向最长回文后缀。

    复杂度为$O(nlogn)$。但是常数较小差不多可以看成大常数$O(n)$

    双倍回文:

    $Description:$

    记字符串x的倒置为$x^R$ 对字符串x,如果满足$x=x^R$ ,则称之为回文;

    如果能够写成$xx^Rxx^R$的形式,则称它是一个「双倍回文」。换句话说,若要x是双倍回文,它的长度必须是$4$的倍数,

    而且前半部分后半部分都要是回文。例如:$ABBA$是一个双倍回文,而$ABAABA$不是,因为它的长度不是$4$的倍数。
    子串是指在S中连续的一段字符所组成的字符串。
    你的任务是,对于给定的字符串,计算它的最长双倍回文子串的长度。$n le 5 imes 10^5$

    我的傻逼做法是用$PAM$跑出它的所有子串,然后$hash$判一半是不是回文。

    然而好一点的做法是,$PAM$之后在树上$dfs$。如果在$dfs$过程中发现长度恰好为一半的点被经过过,那么就合法。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 500005
     4 int f[S],lst,e[27][S],len[S],pc=1,n=1,N,mk[S],ans;char s[S];
     5 #define ull unsigned long long
     6 ull Hsh[S],IHsh[S],pw[S];
     7 int fail(int p){while(s[n]^s[n-len[p]-1])p=f[p];return p;}
     8 void insert(int c){
     9     int t=fail(lst);
    10     if(!e[c][t])f[++pc]=e[c][fail(f[t])],len[e[c][t]=pc]=len[t]+2;
    11     lst=e[c][t];
    12 }
    13 ull hsh(int l,int r){return Hsh[r]-Hsh[l]*pw[r-l];}
    14 ull ihsh(int l,int r){return IHsh[l]-IHsh[r]*pw[r-l];}
    15 int main(){
    16     scanf("%d%s",&N,s+1);len[pw[0]=f[0]=f[1]=1]=-1;
    17     for(int i=1;i<=N;++i)pw[i]=pw[i-1]*29,Hsh[i]=Hsh[i-1]*29+s[i]-'a'+1;
    18     for(int i=N;i;--i)IHsh[i]=IHsh[i+1]*29+s[i]-'a'+1;
    19     #define L len[lst]
    20     for(;n<=N;++n)insert(s[n]-96),ans=max(ans,((L&3)==0&&hsh(n-L/4,n)==ihsh(n-L/2+1,n-L/4+1))*L);
    21     cout<<ans<<endl;
    22 }
    View Code

    最长双回文串:

    $Description:$

    顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。

    输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串

    $2 le |S| le 10^5$

    是板子了。正反都做一遍。要注意判断某一侧回文长度为0则不累加答案。

    被自己加的测试点$Hack$~

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 500005
     4 int pc=1,lst,e[S][26],fail[S],len[S],n=1,E[S],B[S],ans;char s[S];
     5 int Fail(int x,int n){while(s[n]^s[n-len[x]-1])x=fail[x];return x;}
     6 void insert(int c,int n){
     7     int t=Fail(lst,n);
     8     if(!e[t][c])fail[++pc]=e[Fail(fail[t],n)][c],len[e[t][c]=pc]=len[t]+2;
     9     lst=e[t][c];
    10 }
    11 int main(){
    12     scanf("%s",s+1);while(s[n])n++;n--;
    13     fail[0]=fail[1]=1;len[1]=-1;
    14     for(int i=1;i<=n;++i)insert(s[i]-'a',i),B[i]=len[lst];
    15     for(int i=0;i<=pc;++i)fail[i]=len[i]=0;
    16     for(int i=0;i<=pc;++i)for(int c=0;c<26;++c)e[i][c]=0;
    17     fail[0]=fail[1]=pc=1;len[1]=-1;reverse(s+1,s+1+n);
    18     for(int i=1;i<=n;++i)insert(s[i]-'a',i),ans=max(ans,len[lst]&&B[n-i]?len[lst]+B[n-i]:0);
    19     cout<<ans<<endl;
    20 }
    View Code

    Antisymmetry:

    $Description:$

    对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。

    现在给出一个长度为N的01字符串,求它有多少个子串是反对称的。$N le 5 imes 10^5$

    题不错。要求稍微改写$PAM$的模板。在相等改为不等的基础上还有一些细节。

    长度为奇偶,以及位置0的判断条件。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 500005
     4 int fail[S],n,N,e[3][S],dep[S],lst,len[S],pc=1;char s[S];long long ans;
     5 int Fail(int p){while((s[n]^s[n-len[p]-1]^1||len[p]&1)&&p^1)p=fail[p];return p;}
     6 void insert(int c){
     7     int t=Fail(lst);
     8     if(!e[c][t])fail[++pc]=e[c][Fail(fail[t])],len[e[c][t]=pc]=len[t]+2;
     9     lst=e[c][t];dep[lst]=dep[fail[lst]]+(len[lst]&1^1);
    10 }
    11 int main(){
    12     scanf("%d%s",&N,s+1);fail[0]=fail[1]=1;len[1]=-1;
    13     for(n=1;n<=N;++n)insert(s[n]-47),ans+=dep[lst];
    14     cout<<ans<<endl;
    15 }
    View Code

    对称的正方形:

    $Description:$

    Orez很喜欢搜集一些神秘的数据,并经常把它们排成一个矩阵进行研究。最近,Orez又得到了一些数据,并已经把它们排成了一个n行m列的矩阵。通过观察,Orez发现这些数据蕴涵了一个奇特的数,就是矩阵中上下对称且左右对称的正方形子矩阵的个数。 Orez自然很想知道这个数是多少,可是矩阵太大,无法去数。只能请你编个程序来计算出这个数。$n,m le 1000$

    放错专题。作为一道哈希好题还是不错的。

    矩阵哈希也就是个套路,考虑把选中区域左移和上移要乘上什么样的base。

    然后就是枚举回文中心,二分半径。

     1 #include<iostream>
     2 using namespace std;
     3 #define ull unsigned long long
     4 ull Hsh[3][1111][1111],pw1[1111],pw2[1111];int n,m;long long sum;
     5 ull hsh(int o,int u,int d,int l,int r){int c;
     6     if(o==1)c=u,u=n+1-d,d=n+1-c;
     7     if(o==2)c=l,l=m+1-r,r=m+1-c;
     8     u--;l--;int x=r-l,y=d-u;
     9     return Hsh[o][d][r]-Hsh[o][d][l]*pw1[x]-Hsh[o][u][r]*pw2[y]+Hsh[o][u][l]*pw1[x]*pw2[y];
    10 }
    11 #define M (l+r>>1)
    12 int main(){
    13     pw1[0]=pw2[0]=1;cin>>n>>m;
    14     for(int i=1;i<=1000;++i)pw1[i]=pw1[i-1]*29,pw2[i]=pw2[i-1]*31;
    15     for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
    16         cin>>Hsh[0][i][j],Hsh[1][n+1-i][j]=Hsh[2][i][m+1-j]=Hsh[0][i][j];
    17     for(int o=0;o<3;++o)for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)Hsh[o][i][j]+=Hsh[o][i][j-1]*29;
    18     for(int o=0;o<3;++o)for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)Hsh[o][i][j]+=Hsh[o][i-1][j]*31;
    19     for(int x=1;x<=n;++x)for(int y=1;y<=m;++y){
    20         int l=1,r=min(min(x,n-x+1),min(y,m+1-y)),ans;
    21         #define HSH(O) hsh(O,x-M+1,x+M-1,y-M+1,y+M-1)
    22         while(l<=r)if(HSH(0)==HSH(1)&&HSH(0)==HSH(2))l=ans=M,l++;else r=M-1;
    23         sum+=ans;
    24         #undef HSH
    25     }
    26     for(int x=1;x<n;++x)for(int y=1;y<m;++y){
    27         int l=0,r=min(min(x,n-x),min(y,m-y)),ans;
    28         #define HSH(O) hsh(O,x-M+1,x+M,y-M+1,y+M)
    29         while(l<=r)if(HSH(0)==HSH(1)&&HSH(0)==HSH(2))l=ans=M,l++;else r=M-1;
    30         sum+=ans;
    31     }cout<<sum<<endl;
    32 }
    View Code

    顺便提一下manacher吧。

    就是大力分类讨论。离线,常数小。复杂度线性。

    在相邻字符之间插入特殊字符处理奇偶问题。存储已知回文串的最右端点然后暴力匹配。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 22222222
     4 char s[S],rs[S];int ans,n=1,R=-1,C,r[S];
     5 int main(){
     6     scanf("%s",rs+1);while(rs[n])n++;n--;
     7     for(int i=1;i<=n;++i)s[i*2]=rs[i];
     8     n=n*2+1;s[0]='0';
     9     for(int i=1;i<=n;++i){
    10         if(i<=R)r[i]=min(R-i+1,r[2*C-i]);
    11         while(s[i+r[i]]==s[i-r[i]])r[i]++;
    12         if(i+r[i]>R)R=i+r[i]-1,C=i;ans=max(ans,r[i]);
    13     }printf("%d
    ",ans-1);
    14 }
    洛谷模板
  • 相关阅读:
    python,抓取百度搜索结果
    python关键字 with
    python 字符串处理
    采用主动模式进行文件的上传、下载
    系统的备份和恢复
    javascript中的defer
    程序调用批处理完成上传、下载
    vb.net中的ftp上传、拨号、socket通讯的源码
    CF1033G Chip Game
    LG3369 普通平衡树
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12103616.html
Copyright © 2011-2022 走看看