【BZOJ2342】双倍回文(manacher,并查集)
题意:
思路:From http://blog.sina.com.cn/s/blog_8d5d2f04010196bh.html
首先我可以看出:
(1)我们找到的串的本身也是一个回文串(显然)
(2)这个回文串的长度一定是偶数(显然)
(3)左右两个串一定也是偶数长度的回文串(显然)
那么我们先用manacher处理出以每个字符为中心的回文串长度
由于我们所需处理的这些串的长度都为偶数,所以这些串的中心都在manacher时的那些填充字符上(显然)
那么我们就先枚举大串的中心i,设左边小串的中心为j
那么j+rad[j]>=i (rad[]为manacher中处理出的数组)
由于左边一定是回文串,那么rad[j]就应该要覆盖到i(不然怎么保证左边是回文串),而如果左边得到保证,那么右边也一定符合条件(对称)
所以我们就只需求出满足条件的最左侧的j
然后我们对j也有一个枚举范围,那就是在i的回文串范围内,并且还在i-rad[i]/2 ~ i 之间,不然不够
这样我们就可以初步得出一个枚举算法,那就是对于每个i,在一定范围内枚举j,找最优解
据说这个算法是可过的,但是复杂度。。。。似乎不是太乐观
于是需要优化
该优化其实也是显然的
如果我们曾枚举过一个j,它不能覆盖到当前枚举的i(也就是j+rad[j]
那么这个j,用一定不能覆盖到i+1(显然)
也就是说这个j在之后的计算中都没有用了,我们就不需要枚举了
这样我们就可以在枚举j的时候一段一段的跳,以降低复杂度
而实现这个过程,我们可以用并查集
每次都将没用的j的父亲指向j+1,然后跳到getfather(j+1)
这样就轻松完成了分段跳这个优化
最后在分析一下复杂度
(1)manacher O(n)
(2)并查集 O(nα(n))
(3)每个点最多被删n次 O(n)
(4)每个点最多被利用一次 O(n)
(5)每个点最多被枚举一次 O(n)
这个复杂度真的是怎么算怎么舒心,而且代码很好实现
1 var f,p:array[0..1100000]of longint; 2 a:array[0..1100000]of char; 3 len,i,n,mx,id,ans,j:longint; 4 ch:ansistring; 5 6 function min(x,y:longint):longint; 7 begin 8 if x<y then exit(x); 9 exit(y); 10 end; 11 12 function max(x,y:longint):longint; 13 begin 14 if x>y then exit(x); 15 exit(y); 16 end; 17 18 function find(k:longint):longint; 19 begin 20 if f[k]<>k then f[k]:=find(f[k]); 21 find:=f[k]; 22 end; 23 24 begin 25 assign(input,'bzoj2342.in'); reset(input); 26 assign(output,'bzoj2342.out'); rewrite(output); 27 readln(len); 28 readln(ch); 29 n:=2; a[1]:='@'; a[2]:='#'; 30 for i:=1 to len do 31 begin 32 inc(n); a[n]:=ch[i]; 33 inc(n); a[n]:='#'; 34 end; 35 inc(n); a[n]:='$'; 36 mx:=0; id:=0; 37 for i:=2 to n-1 do 38 begin 39 if mx>i then p[i]:=min(p[id*2-i],mx-i) 40 else p[i]:=1; 41 while a[i-p[i]]=a[i+p[i]] do inc(p[i]); 42 if p[i]+i>mx then 43 begin 44 mx:=p[i]+i; 45 id:=i; 46 end; 47 end; 48 for i:=2 to n-1 do 49 if a[i]='#' then f[i]:=i 50 else f[i]:=i+1; 51 i:=2; j:=2; 52 repeat 53 i:=i+2; 54 if i>n then break; 55 j:=find(max(i-p[i] div 2,2)); 56 while (j<i)and(j+p[j]<i) do 57 begin 58 f[j]:=find(j+1); 59 j:=f[j]; 60 end; 61 if j<i then ans:=max(ans,(i-j)*2); 62 until i>n; 63 writeln(ans); 64 close(input); 65 close(output); 66 end.
null