zoukankan      html  css  js  c++  java
  • 【题解】回文串 APIO 2014 BZOJ 3676 COGS 1985 Manacher+后缀数组+二分

    这题可以用回文自动机来做,但是我并没有学,于是用Manacher+SA的做法O(nlogn)水过

    首先,看到回文串就能想到用Manacher

    同样还是要利用Manacher能不重复不遗漏地枚举每个回文子串的性质

    只是不重复不遗漏还不够,我们还要统计出现次数

    每个子串一定是一个后缀的前缀,于是可以用后缀数组

    用后缀数组求出height数组之后,对于在Manacher过程中枚举到的每个长度为k的回文串,可以在height数组中二分,用O(logn)的时间求出这个子串的出现次数

    BZOJ和COGS上有评论说Manacher + SA的方式被卡了,也有人说自己跑了19s,我这个实现是在BZOJ上跑了10s,COGS的76组数据总共跑了3.7s。

    代码如下:

      1 #include <cstring>
      2 #include <algorithm>
      3 #include <cstdio>
      4 #include <cctype>
      5 
      6 using namespace std;
      7 typedef long long ll;
      8 const int MAXN = 300010, LOGN = 20;
      9 
     10 int n;
     11 char str[MAXN];
     12 int sas[MAXN], san;
     13 int mas[MAXN<<1], man;
     14 
     15 namespace SA {
     16     int sa[MAXN], rk[MAXN], ht[MAXN];
     17     int tmp1[MAXN], tmp2[MAXN], cnt[MAXN];
     18     int minv[MAXN][LOGN], logn[MAXN];
     19     void solve( int m ) {
     20         int *x = tmp1, *y = tmp2;
     21         for( int i = 0; i < m; ++i ) cnt[i] = 0;
     22         for( int i = 0; i < san; ++i ) ++cnt[ x[i] = sas[i] ];
     23         for( int i = 1; i < m; ++i ) cnt[i] += cnt[i-1];
     24         for( int i = san-1; i >= 0; --i ) sa[--cnt[x[i]]] = i;
     25         for( int k = 1; k <= san; k <<= 1 ) {
     26             int p = 0;
     27             for( int i = san-k; i < san; ++i ) y[p++] = i;
     28             for( int i = 0; i < san; ++i ) if( sa[i] >= k ) y[p++] = sa[i]-k;
     29             for( int i = 0; i < m; ++i ) cnt[i] = 0;
     30             for( int i = 0; i < san; ++i ) ++cnt[x[i]];
     31             for( int i = 1; i < m; ++i ) cnt[i] += cnt[i-1];
     32             for( int i = san-1; i >= 0; --i ) sa[--cnt[x[y[i]]]] = y[i];
     33             swap(x,y), x[sa[0]] = 0, p = 1;
     34             for( int i = 1; i < san; ++i )
     35                 x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k] ? p-1 : p++;
     36             if( p == san ) break;
     37             m = p;
     38         }
     39         for( int i = 0; i < san; ++i ) rk[i] = x[i];
     40         int k = 0;
     41         for( int i = 0; i < san; ++i ) {
     42             if( k ) --k;
     43             if( !rk[i] ) continue;
     44             int j = sa[rk[i]-1];
     45             while( sas[i+k] == sas[j+k] ) ++k;
     46             ht[rk[i]] = minv[rk[i]][0] = k;
     47         }
     48         for( int k = 1; (1<<k) <= san; ++k )
     49             for( int i = 0; i+(1<<k) <= san; ++i )
     50                 minv[i][k] = min( minv[i][k-1], minv[i+(1<<(k-1))][k-1] );
     51         k = 0;
     52         for( int i = 1; i <= san; ++i ) {
     53             if( (1<<(k+1)) <= i ) ++k;
     54             logn[i] = k;
     55         }
     56     }
     57     int qmin( int l, int r ) {
     58         int k = logn[r-l+1];
     59         return min( minv[l][k], minv[r+1-(1<<k)][k] );
     60     }
     61 }
     62 
     63 void input() {
     64     scanf( "%s", str ), n = strlen(str);
     65     man = 0;
     66     for( int i = 0; i < n; ++i ) {
     67         sas[i] = str[i];
     68         mas[man++] = '#', mas[man++] = str[i];
     69     }
     70     sas[n] = 0, san = n+1;
     71     mas[man++] = '#';
     72     SA::solve(128);
     73 }
     74 
     75 ll ans = 1;
     76 int rd[MAXN<<1];
     77 void update( int p, int k ) {
     78     using namespace SA;
     79     p = rk[p];
     80     int LL = 0, LR = p;
     81     while( LL < LR ) {
     82         int mid = (LL+LR)>>1;
     83         if( qmin(mid+1,p) >= k ) LR = mid;
     84         else LL = mid+1;
     85     }
     86     int RL = p, RR = san-1;
     87     while( RL < RR ) {
     88         int mid = (RL+RR+1)>>1;
     89         if( qmin(p+1,mid) >= k ) RL = mid;
     90         else RR = mid-1;
     91     }
     92     ans = max( ans, ll(k)*(RL-LL+1) );
     93 }
     94 int cnt[26] = {0};
     95 void manacher() {
     96     int mx = 0, p = 0;
     97     for( int i = 0; i < man; ++i ) {
     98         if( i < mx ) rd[i] = min( rd[2*p-i], mx-i );
     99         else rd[i] = 1;
    100         while( i+rd[i] < man && i-rd[i] >= 0 && mas[i+rd[i]] == mas[i-rd[i]] ) {
    101             if( islower( mas[i-rd[i]] ) ) update( (i-rd[i])/2, rd[i]+1 );
    102             ++rd[i];
    103         }
    104         if( i+rd[i] > mx ) mx = i+rd[i], p = i;
    105     }
    106     for( int i = 0; i < n; ++i )
    107         ans = max( ans, (ll)++cnt[str[i]-'a'] );
    108     printf( "%lld
    ", ans );
    109 }
    110 
    111 int main() {
    112     // freopen( "apio2014_palindrome.in", "r", stdin );
    113     // freopen( "apio2014_palindrome.out", "w", stdout );
    114     input(), manacher();
    115     return 0;
    116 }
  • 相关阅读:
    每日日报
    剑指 Offer 18. 删除链表的节点(LeetCode)
    java的访问权限
    java从键盘输入
    剑指 Offer 22. 链表中倒数第k个节点(快慢指针)(LeetCode)
    面试题 02.03. 删除中间节点(LeetCode)
    21. 合并两个有序链表(Leetcode)
    计算总线数据传输率
    时钟周期、总线周期(机器周期)区别
    书单(个人)
  • 原文地址:https://www.cnblogs.com/mlystdcall/p/6533903.html
Copyright © 2011-2022 走看看