1. 程式人生 > >Manacher演算法(一個字串中找到最長迴文子串)

Manacher演算法(一個字串中找到最長迴文子串)

零、預備知識

  Manacher用於在一個字串中找到最長的迴文子串

  迴文串:正著念和反著念一樣,例如aabbaa,anna等。

  注意子串與子序列的區別:

    子串必須是在原字元中可以找到的。比如 " I am a student"。am是子串(當然也是子序列),但是aa就不是子串了(是子序列)。

一、演算法原理

  Manacher演算法通常人稱馬拉車演算法,用於在一個字串中找到最長的迴文子串。

  1.首先因為奇偶個數的不同,所以判斷迴文的方式不一樣,因此manacher演算法通過在字串兩邊和每個字元之間插入一個相同的字元來字串轉換為奇數個的字串。插入的字元可以是任意字元,但                   是必須保證插入的字元都相同,一般常用‘#’作為插入字元。例如,原始字串aaabbbaaa,插入後的字串則為#a#a#a#b#b#b#a#a#a#。

  2.manacher演算法使用了一個輔助陣列pArr[](迴文陣列)來儲存以每個位置i為中心的最長迴文串的最右邊界到位置i的距離。假設最長迴文串左右邊界分別為L,R,那麼pArr[i]=R-i+1;

  3.接下來就是計算字串的迴文陣列pArr[],計算時分兩大情況:

    3.1當前位置i不在迴文右邊界中,則向右邊暴力擴張。

    3.2當前位置i在迴文右邊界內(又分為三種情況):

      3.2.1:i的迴文半徑徹底在R(以C(迴文中心)為中心所能到達的最右邊界)內,則R不用擴

      例如:

      3.2.2:i ’  的迴文半徑在L外面,每包住,此時i的迴文半徑為i到R。

      例如:

      3.2.3:i'的迴文半徑壓線,i與R之間的半徑不用計算,但是不確定是否再向右擴

二、演算法實現

 1 public class Manacher {
 2     public static char[] manacherString(String str){  
 3         char[] charArr = str.toCharArray();
 4         char[] res = new char[str.length()*2+1];
 5         int index = 0;
 6         for(int
i=0;i<res.length;i++){//01 10&01 100&001 7 res[i] = (i&1)==0?'#':charArr[index++]; 8 } 9 return res; 10 } 11 12 public static int maxLcpsLength(String str){ 13 if(str==null||str.length()==0){ 14 return 0; 15 } 16 char[] charArr = manacherString(str);//擴充後的字串 17 int[] pArr = new int[charArr.length];//儲存每個位置的最大回文長度 18 int index = -1;//迴文右邊界中心 2*index-i是i位置關於index的對稱點 19 int pR = -1;//目前所能到達的最右邊界 20 int max = Integer.MIN_VALUE; 21 for(int i=0;i!=charArr.length;i++){//對每個位置 22 pArr[i] = pR>i?Math.min(pArr[2*index-i], pR-i):1;//起碼迴文的距離,看不用擴的區域是多少,是與之對稱的點的迴文半徑限制了它還是它距離最右邊界的距離限制了它 23 while(i+pArr[i]<charArr.length&&i-pArr[i]>-1){//如果在範圍內則往外擴 24 if(charArr[i+pArr[i]]==charArr[i-pArr[i]]){ 25 pArr[i]++; 26 }else{ 27 break; 28 } 29 } 30 if(i+pArr[i]>pR){ 31 pR = i+pArr[i]; 32 index = i; 33 } 34 max = Math.max(max, pArr[i]); 35 } 36 return max-1; 37 } 38 39 public static void main(String[] args) { 40 //String str = "abc1234321ab"; 41 String str = "1213121"; 42 System.out.println(maxLcpsLength(str)); 43 } 44 }