题目
思路
一个更通用的思路:将回文视作标记 ( l , r , k ) (l,r,k) (l,r,k) 表示 [ l , l + k ) [l,l+k) [l,l+k) 和 [ r , r + k ) [r,r+k) [r,r+k) 这两段子串在翻转后相同。用带权并查集维护(为 1 1 1 表示翻转后相等,为 0 0 0 表示直接相等。权值为到根节点的路径异或和),让 k = 2 p k=2^p k=2p 即可转移到 k = 2 p − 1 k=2^{p-1} k=2p−1 的情况。
最后处理不相等的字符,这个应当是比较简单的。时间复杂度 O ( α n log n ) \mathcal O(\alpha n\log n) O(αnlogn) 。
但是这个题是字符串题。可以联想到 m a n a c h e r \rm manacher manacher 的过程——对于上面的那种标记传递的问题,其实并不存在。为什么我们要用 S T \rm ST ST 表呢?因为有一个传递闭包的问题。可能 a , b a,b a,b 与 c c c 距离甚远,但是 a = c a=c a=c 且 b = c b=c b=c,所以 a = b a=b a=b,这就需要提前预知才行。
而 m a n a c h e r \rm manacher manacher 告诉我们,根本没必要。譬如 [ a , b ] [a,b] [a,b] 是回文且 [ c , d ] [c,d] [c,d] 是回文,不妨设 a ⩽ c a\leqslant c a⩽c,如果 b ⩽ d b\leqslant d b⩽d 即两个区间只是相交,那么从左往右依次确定字符并没有什么问题(不需要提前预知信息);如果 d ⩽ b d\leqslant b d⩽b 即两个区间相互包含,那么 [ c , d ] [c,d] [c,d] 沿着 a , b a,b a,b 中轴翻转后得到的要求(所谓传递闭包)已然存在。毕竟保证有解。
也就是说:只需要贪心地让当前位置的回文长度是 r i r_i ri 就够了。
类似的道理,判断字符不相等,只需要 存下每一位不能为何字符,当扫描到它的时候再判断。
时间复杂度 O ( n ) \mathcal O(n) O(n) 。
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long int_;
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; '0'>c||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
void writeUnsigned(const unsigned &x){
if(x > 9) writeUnsigned(x/10);
putchar((x-x/10*10)^48);
}
inline void writeint(const int &x){
if(x >= 0) writeUnsigned(x);
else putchar('-'), writeUnsigned(-x);
}
inline void getMin(int &a,const int &b){ if(b < a) a = b; }
inline void getMax(int &a,const int &b){ if(a < b) a = b; }
const int MAXN = 200005;
char str[MAXN]; int r[MAXN];
unsigned ok[MAXN];
int main(){
int n = readint();
for(int i=2; i<=(n<<1); i+=2)
r[i] = readint();
for(int i=3; i<(n<<1); i+=2)
r[i] = readint(), str[i] = '#';
str[1] = str[n<<1|1] = '#';
str[0] = '!', str[(n+1)<<1] = '?';
for(int i=2,o=1,x=0; i<=(n<<1); ++i,x=0){
if(o+r[o] > i) // get symmetry
x = min(o+r[o]-i,r[(o<<1)-i]);
if(str[i] == 0) // unset
str[i] = char(__builtin_ffs(~ok[i])-1+'a');
for(; x<=r[i]; ++x) str[i+x] = str[i-x];
if(x != i) // not left boundary
ok[i+x] |= (1u<<(str[i-x]-'a'));
if(i+r[i] > o+r[o]) o = i;
}
rep(i,1,n) putchar(str[i<<1]);
putchar('\n');
return 0;
}