考虑到暴力求解可能会超时,manacher算法目的就是减少重复的遍历,减小时间复杂度,暴力求解时间复杂度是O(n^2),manacher算法可提升为O(N),因为manacher在遍历的时候只会往后面未遍历的字符进行暴力求解式对比,理解为要查询的当前字符本身处在一个前面遍历成功查询到的最长回文段内,如 i<mx(后面会详细讲到,也可结合代码),前面有对称点j则直接相等相应的最长回文长度,若要验证超出 j 范围的必须往后面未遍历的字符查询,因此 i 是一直向前查询,O(n)
关键:分类讨论当前字符所在区间和本身最长回文
1.i>mx在前一个id最长回文之外,那和普通暴力求解过程一样
2.i<=mx 即在前一个id最长回文内,能找到对称点j
2.1 j本身的最长回文长度区间仍在 id回文内,那么i的最长回文至少是对称点j的长度,但因为i周围环境与j不同,还需检测i是否有更长的回文,到下一步
2.2 j本身的最长回文有部分在id内有部分超出,那i可以确定的只有j在id回文内的那一段是相等的,超出部分由于ij周围环境不一定相等,不可等同,需同上述进行自我检测
总结:两者合起来就是找两个中最长回文值最小的作为基础保底值,后面的长度需要自我检测
1 #include<stdio.h> 2 #include<string.h> 3 #define max 1000000 4 5 char str[max],s[max]; 6 int len[max]={0} ; 7 8 void init() 9 { 10 int n,i=1,j=0; 11 str[0]='$'; //加$是为了分界,在i的前n个字符和后n个字符匹配时,由于$是唯一的,所以匹配到开头时就会停止匹配 12 n=strlen(s); 13 str[2*n+1]='#'; 14 15 while(i<2*n+1){ 16 str[i++]='#'; 17 str[i++]=s[j++]; 18 } 19 } 20 21 int min(int x,int y) 22 { 23 int z=x<=y?x:y; 24 return z; 25 } 26 27 int manacher() 28 { 29 int i,n,mx=0,id=0,maxlen=0; 30 n=strlen(s); 31 32 for(i=1;i<=2*n+1;i++) 33 { int j=2*id-i; 34 if(i<mx) len[i]=min(mx-i,len[j]); 35 else len[i]=1; //判断i是否有对称的j可直接相等,若i=mx则就算有相对应的j长度也只是1,还是要进行下一步的暴力求解 36 37 38 while(str[i+len[i]]==str[i-len[i]]) //暴力求解检测超出j范围的最长回文是否存在,即j存在,i至少是len[j]长,但有可能比j还长,因为j是已经查询过的已经固定的 39 len[i]++; 40 41 if(i>mx) //超出回文范围则更新id 42 { 43 id=i; 44 mx=id+len[i]-1; 45 } 46 47 if(maxlen<len[i]) //记录最长回文长度 48 { 49 maxlen=len[i]-1; 50 } 51 } 52 return maxlen; 53 } 54 55 int main() 56 { 57 58 scanf("%s",&s); 59 int n=strlen(s); 60 printf(" n=%d ",n); 61 init(); 62 printf("%s",str); 63 int l= manacher(); 64 printf(" "); 65 66 67 for(int i=0;i<=2*n+1;i++) 68 { 69 printf("%d",len[i]); 70 } 71 72 printf(" %d",l); 73 } 74