这题可以用回文自动机来做,但是我并没有学,于是用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 }