回文串就是乙個正讀和反讀都一樣的字串,比如「level」或者「noon」等等就是回文串。回文子串,顧名思義,即字串中滿足回文性質的子串。比如輸入字串 "google」,由於該字串裡最長的對稱子字串是 "goog」,因此輸出4。
1.問題解決的基本方法
分析:可能很多人都寫過判斷乙個字串是不是對稱的函式,這個題目可以看成是該函式的加強版。
要判斷乙個字串是不是對稱的,不是一件很難的事情。我們可以先得到字串首尾兩個字元,判斷是不是相等。如果不相等,那該字串肯定不是對稱的。否則我們接著判斷裡面的兩個字元是不是相等,以此類推。
/*現在我們試著來得到對稱子字串的最大長度。最直觀的做法就是得到輸入字串的所有子字串,並逐個判斷是不是對稱的。如果乙個子字串是對稱的,我們就得到它的長度,最後經過比較,就能得到最長的對稱子字串的長度了。*說明:求最長回文字串
*日期:2013-10-15
*/#include
using
namespace
std;
//字串是否對稱
bool isaym(char *cbegin, char *cend)
while(cbegin
cbegin++;
cend--;
}return
true
;}
//上述方法的時間效率:由於需要兩重while迴圈,每重迴圈需要o(n)的時間。另外,我們在迴圈中呼叫了issym,每次呼叫也需要o(n)的時間。因此整個函式的時間效率是o(n^3)。o(n*n*n)複雜度的子字串
int getmaxsym(char *str)
}plast --;
}pfirst ++;
}return
maxlength;
}
假設輸入:abcddcba,按照上述程式,要分割成 'abcddcba』, 'bcddcb』, 'cddc』, 'dd』…等字串,並對這些字串分別進行判斷。不難發現,很多短子字串在長些的子字串中比較過,這導致了大量的冗餘判斷,根本原因是:對字串對稱的判斷是由外向裡進行的。
換一種思路,從裡向外來判斷。也就是先判斷子字串(如dd)是不是對稱的。如果它(dd)不是對稱的,那麼向該子字串兩端各延長乙個字元得到的字串肯定不是對稱的。如果它(dd)對稱,那麼只需要判斷它(dd)兩端延長的乙個字元是不是相等的,如果相等,則延長後的字串是對稱的。
2.改進的解決方案
根據從裡向外比較的思路寫出如下**:
//由於子字串的長度可能是奇數也可能是偶數。長度是奇數的字串是從只有乙個字元的中心向兩端延長出來,而長度為偶數的字串是從乙個有兩個字元的中心向兩端延長出來。因此程式中要把這兩種情況都考慮進去。改進後的程式
int getmaxsym2(char *str)
if(oddlenght >maxlength)
//偶數子字串
left =ptag;
right = ptag + 1
;
int evenlength = 0
;
while(left >= str && *right != '
\0' && *left == *right)
if(evenlength >maxlength)
ptag++;
}return
maxlength;
}
由於總共有o(n)個字元,每個字元可能延長o(n)次,每次延長時只需要o(1)就能判斷出是不是對稱的,因此整個函式的時間效率是o(n^2)。
上述方法稱為樸素演算法,關於字串的題目常用的演算法有kmp、字尾陣列、ac自動機,這道題目利用擴充套件kmp可以解答,其時間複雜度也很快o(n*logn)。但是,這裡介紹乙個專門針對回文子串的演算法,其時間複雜度為o(n),這就是manacher演算法。
3.manacher演算法
演算法的基本思路是這樣的:把原串每個字元中間用乙個串中沒出現過的字元分隔#開來(統一奇偶),同時為了防止越界,在字串的首部也加入乙個特殊符$,但是與分隔符不同。同時字串的末尾也加入'\0'。演算法的核心:用輔助陣列p記錄以每個字元為核心的最長回文字串半徑。也就是p[i]記錄了以str[i]為中心的最長回文字串半徑。p[i]最小為1,此時回文字串就是字串本身。
示例:原字串 'abba』,處理後的新串 ' $#a#b#b#a#\0』,得到對應的輔助陣列p=[0,1,1,2,1,2,5,2,2,1]。
程式如下,對應的變數解釋在後面
//以下是manacher演算法的具體實現,包括:輔助陣列的構建、最大字串長度的獲取。預處理,將str:abba轉換為: $#a#b#b#a#\0(從1開始)
char * pre(char *str)
prestr[
2*length+2]='#'
; prestr[
2*length+3]='\0'
;
return
prestr;
}
//上面幾個變數說明:pi記錄具有遍歷過程中最長半徑的回文字串中心字串。mx記錄了具有最長回文字串的右邊界。manacher演算法
int getmaxsym3(char *str)
else
while(prestr[i-p[i]]==prestr[i+p[i]]&&i-p[i]>0&&i+p[i]
if(i+p[i] >mx)
}//最大回文字串長度
int maxlen = 0
;
for(int i=0;i)
}delete prestr;
delete p;
return maxlen - 1
;}
pi是最長回文字串(淡藍色)的中心,如果以j為中心的最大回文串如上如所示,那麼i處的情況與j處相同(關於pi的兩側是對稱的)。這樣便減少了運算量,i的對稱位置是2*pi-i。
但是有另外一種情況,就是j的一部分超出藍色部分,這時p[i]=p[j]就不一定對了,如下圖
這就為什麼有取最小值這個操作:
if(mx>i)剩下的**就很容易看懂了。
最後遍歷一邊p陣列,找出最大的p[i]-1就是所求的最長回文字串長度,說明如下:
(1)因為p[i]記錄插入分隔符之後的回文字串半徑,所以以i為中心的回文字串長度為2*p[i]-1。例如:bb=>#b#b#,中間#的半徑為3,回文字串長度為2*3-1;
(2)注意上面兩個串的關係。 #b#b#減去乙個#號的長度就是原來的2倍。即((2*p[i]-1)-1)/2 = p[i]-1,得證。
最長回文字串
scanf s 輸入字串碰到空格或者tab就會停下來。此處可以使用fgets或者gets 另外注意標頭檔案cctype中的函式的巧妙使用,此處使用isalpha和toupper簡化了 此處列舉字串的中間位置,然後向倆邊擴充套件,節省了時間複雜度,注意向倆邊擴充套件時,奇數個和偶數個長度的區別。另外程...
最長回文字串
回文串定義 回文串 是乙個正讀和反讀都一樣的字串,比如 asddsa 或者 lovekevol 等等就是回文串。回文子串,顧名思義,即字串中滿足回文性質的子串。這裡我給出通過 列舉回文串的中間位置i,然後不斷向外擴充套件,直達有字元不相同。注意,這裡長度為奇數和偶數的處理方式是不一樣的。下面給出 這...
最長回文字串
時間限制 1000 ms 記憶體限制 65535 kb 難度 4 描述 輸入乙個字串,求出其中最長的回文子串。子串的含義是 在原串連續出現的字串片段。回文的含義是 正著看和倒著看是相同的,如abba和abbebba。在判斷是要求忽略所有的標點和空格,且忽略大小寫,但輸出時按原樣輸出 首尾不要輸出多餘...