【题解】回文串 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\n", 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 }

 

posted @ 2017-03-11 08:50  mlystdcall  阅读(423)  评论(0编辑  收藏  举报