这道题有很多人用的是KMP的算法,据说KMP的时间复杂度是O(n*logn),但是用Manacher,就是O(n), 所以力挺MANA
这里面和正常求最长回文串不同的是,这个回文串要前半部分单调递增,当然,由于对称性,后半部分单调递减!
那么就要在原来的基础上,加上几个判断,保证回文串是满足以上要求的。
首先说,最右侧在sign的位置,那么一定是要将最长回文串长度加1的,这是显而易见的,对称嘛;
其次说,如果是在回文串数据的位置,就要在最右面或最左边,减2或加2来对比是不是满足要求。而这里有两种可能,如果当前的数字是数据的话,那就只要判断数据大小,但是如果是sign的话,那么就要判断最右侧的位置减2的位置在哪里,可能在当前位置的右侧,就相当于原串有这样的子串,“23 23”,这样的话也当然要加1了,然后接下来要判断的是再进一步扩展的数据的大小。
画个图就会发现一共就这三种可能,尤其是sign两侧的相同的数据,这个可能要好好考虑,不能落下
代码:
//最长回文串 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 100100; int T, n, id, Maxl, Maxid, p[N<<1]; int s[N], str[N<<1]; void kp() { for ( int i = 1; i < n; ++i ) { if ( Maxid > i ) p[i] = min( Maxid-i, p[id*2-i] ); else p[i] = 1; while ( str[i+p[i]] == str[i-p[i]] ) { if ( str[i+p[i]] == 2 ) p[i]++; else if ( ( i+p[i]-2 < i ) || str[i+p[i]] <= str[i+p[i]-2] ) p[i]++; else break; } if ( Maxid < p[i] + i ) { Maxid = p[i]+i; id = i; } if ( p[i] > Maxl ) Maxl = p[i]; } } int main() { while ( scanf("%d", &T) != EOF ) { while ( T-- ) { scanf("%d", &n); memset( str, 0, sizeof(str) ); str[0] = -1; str[1] = 2; int j = 2; for ( int i = 0; i < n; ++i ) { scanf("%d", &str[j++]); str[j++] = 2; } n = j; str[n] = -2; //printf("%d\n", n); //for ( int i = 0; i < n; ++i ) printf(" %d", str[i]); id = Maxid = Maxl = 0; kp(); printf("%d\n", Maxl - 1); } } }