bzoj2342

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.
View Code
原文地址:https://www.cnblogs.com/phile/p/4473174.html