Post

manacher

用途

$ O(n)求最长回文串 $

思路

  • 在字符串前面加$#,后面加^,相邻字符之间添加#
  • 可以发现原字符串的回文串都会转换为新字符串的奇数回文串
  • 并且原回文串长度=新回文串半径-1
  • $ 定义p_i为以i为中心的回文串的半径 $
  • $ 假设已经找出一个回文串,中心为mid,mr为该半径+1 $
  • $ 若i<mr ,代表当前段在已经找到的回文串内 $
    • $ 找出相对于mid对称的点j=2*mid-i ,p_j已知$
    • $ 则p_i=min(p_j,mr-i) 不能超过当前大的回文串长度(超过的段无法保证回文,没超过则P_i=p_j)$
  • $ 暴力更新p_i长度 $
  • $ 若i+p_i>mr 更新mid和mr $
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    string a,s;
    int p[N],n;
    void init(){
      s="";
      s+="$#";
      for(auto c:a)s+=c,s+='#';
      s+="^";
      n=s.size();
    }
    void manacher(){
      int mr=0,mid;
      for(int i=1;i<n;i++){
          if(i<mr)p[i]=min(p[2*mid-i],mr-i);
          else p[i]=1;
          while(s[i-p[i]]==s[i+p[i]])p[i]++;
          if(i+p[i]>mr){
              mr=i+p[i];
              mid=i;
          }
      }
    }
    void solve(){
      cin>>a;
      init();
      manacher();
      int res=0;
      for(int i=1;i<n;i++)res=max(res,p[i]-1);
      cout<<res;
    }
    

    P1659 [国家集训队] 拉拉队排练

    题意

    给定一字符串,求出所有回文串,对奇数长度排序从大到小,求前k个奇数回文串长度的乘积

    思路

    manacher预处理,注意是所有回文串不是每个最长回文串
    倒数枚举,累计求和,快速幂计算,因为如果有cnt[n]个最长回文串,则cnt[n-2],cnt[n-4]…都应加上未算的cnt[n]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    
    string a,s;
    ll n,k,p[N],cnt[N];
    void init(){
      s="";
      s+="$#";
      for(auto c:a)s+=c,s+='#';
      s+="^";
      n=s.size();
    }
    void manacher(){
      int mr=0,mid;
      for(int i=1;i<n;i++){
          if(i<mr)p[i]=min(p[2*mid-i],1ll*mr-i);
          else p[i]=1;
          while(s[i-p[i]]==s[i+p[i]])p[i]++;
          if(i+p[i]>mr){
              mr=i+p[i];
              mid=i;
          }
          if(p[i]%2==0)cnt[p[i]-1]++;
      }
    }
    ll qpow(ll a,ll b){
      ll ans=1;
      for(;b;b>>=1){
          if(b&1)ans=ans*a%mod;
          a=a*a%mod;
      }
      return ans;
    }
    void solve(){
      cin>>n>>k>>a;
      init();
      manacher();
      ll res=1,sum=0;
      for(int i=n;i>=1;i--){
          if(i%2==0)continue;
          sum+=cnt[i];
          if(k>=sum){
              res=res*qpow(i,sum)%mod;
              k-=sum;
          }
          else{
              res=res*qpow(i,k)%mod;
              k=0;
              break;
          }
      }       
      if(k>0){
          cout<<"-1\n";
          return;
      }
      cout<<res;
    }
    
This post is licensed under CC BY 4.0 by the author.