shoi题目好坑爹
首先自己测发现这道题如果用后缀数组+rmq处理每个点回文串能延伸长度的话会TLE
(当然我用的是倍增+ST的方法,如果用三分构建后缀数组+笛卡尔树处理rmq我就不知道了);
关于最长回文子串的问题有一个更快的算法叫manacher,实现简单也好理解
这个算法的优点在于它在每两个字符之间插入一个新的字符#,使得所有的回文子串都变成了奇数长度的
具体的介绍:http://blog.csdn.net/ggggiqnypgjg/article/details/6645824
求出以每个字符为中心的回文串最长延伸长度p[i]后(注意p[i]-1就是在原串中以该字符为中心的最长回文子串)
我们下面就要来找双倍回文
首先,穷举中心字符我们肯定是穷举“#”而不穷举字母
不难想到一种O(n2)的做法,先穷举双倍回文的中心,在穷举左半边回文的中心判断是否存在可行解
(事实上,左边穷举的时候我们只要从i-p[i]/2找就可以了
但实际上,随着我们向右穷举双倍回文的中心,左边一些点穷举了一次是肯定是不用再穷举的,
我们可以考虑用并查集优化
1 var p,fa:array[0..1000010] of longint; 2 j,l,n,k,i,ans,right:longint; 3 s:array[0..1000010] of char; 4 ch:char; 5 6 function min(a,b:longint):longint; 7 begin 8 if a>b then exit(b) else exit(a); 9 end; 10 11 function max(a,b:longint):longint; 12 begin 13 if a>b then exit(a) else exit(b); 14 end; 15 16 function getf(x:longint):longint; 17 begin 18 if x<>fa[x] then fa[x]:=getf(fa[x]); 19 exit(fa[x]); 20 end; 21 22 begin 23 readln(l); 24 s[0]:='$'; 25 s[1]:='#'; 26 for i:=1 to l do 27 begin 28 read(ch); 29 s[i*2]:=ch; 30 s[i*2+1]:='#'; 31 end; 32 n:=2*l+1; 33 k:=0; 34 right:=0; 35 for i:=1 to n do 36 begin 37 if right>i then 38 p[i]:=min(p[2*k-i],right-i) 39 else p[i]:=1; 40 while (s[i+p[i]]=s[i-p[i]]) do inc(p[i]); 41 if p[i]+i>right then 42 begin 43 right:=p[i]+i; 44 k:=i; 45 end; 46 end; 47 for i:=1 to n do 48 if s[i]='#' then fa[i]:=i else fa[i]:=i+1; 49 for i:=3 to n do 50 begin 51 if i mod 2=1 then 52 begin 53 j:=getf(max(i-p[i] div 2,1)); 54 while (j<i) and (j+p[j]-1<i) do 55 begin 56 fa[j]:=getf(j+1); 57 j:=fa[j]; 58 end; 59 if j<i then ans:=max(ans,(i-j)*2); 60 end; 61 end; 62 writeln(ans); 63 end.