題目
給定字符串str,若子串s是迴文串,稱s爲str的迴文字串。設計算法,計算str的最長迴文字串。
枚舉所有字串,顯然是一種解法,但效率太過低下。
算法解析
- 因爲迴文串有奇數和偶數的不同,判斷一個串是否是迴文串,往往要分開編寫,造成代碼拖沓。
- 一個簡單的事實:纏度爲n的字符串,共有n-1個“鄰接”,加上首字符的前面,和末字符的後面,共n+1的“空”。因此,字符串本身和“空”一起,共有2n+1個,必定是奇數。如abbc-〉#a#b#c#;aba-〉#a#b#a#
- 因此,將帶計算母串擴展成gap串,計算迴文字串的過程中,只考慮奇數匹配即可。
- 字符串12212321-〉s[]="$#1#2#2#1#2#3#2#1#";爲了處理統一,最前面加一位未出現的字符,如$
- 用一個數組P[i]來記錄以字符S[i]爲中心的最長迴文字串向左/右擴張的長度(包括S[i]),比如S和P的對應關係:
- S # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #
- P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
- P[i]-1正好是原字符串中迴文串的總長度
下面介紹一下回文算法的核心算法思想Manacher算法
如下圖
- 這裏需要求的是i的迴文字串的個數,mx爲目前匹配的最遠的位置,爲0到i-1中k+P[k]最大的數,代碼裏有體現,現在已知的是i以前的所有迴文字串是求出來的。
- 記i關於id的對稱點位j(=2*id-i),若此時滿足條件mx-i〉P[j];則有如下:
- 記my爲mx關於id的對稱點(my=2*id-mx);
- 由於以S[id]爲中心的最大回文子串爲S[my+1...id...mx-1],即:S[my+1,...,id]與S[id,...,mx-1]對稱,而i和j關於id對稱,因此P[i]=P[j](P[j]是已知的)。
- 記關於id的對稱點爲j(=2*id-i),若此時滿足條件mx-i〈P[j],則有如下:
- 記my爲mx關於id的對稱點(my=2*id-mx);
- 由於以S[id]爲中心的最大回文子串爲S[my+1...id...mx-1],即:S[my+1,...,id]與S[id,...,mx-1]對稱,而i和j關於id對稱,因此P[i]至少等於mx-i(途中綠色框部分)。
代碼如下所示
void Manacher(char* s, int* P)
{
int size = strlen(s);
P[0] = 1;
int id = 0;
int mx = 1;
for (int i = 1; i < size; i++)
{
if (mx > i)//mx比i大,說明i被mx控制住了
{
P[i] = P[2*id - i] ? P[2*id - i] < (mx - i);
}
else
{
P[i] = 1;
}
for (; s[i + P[i]] == s[i - P[i]]; P[i]++);
if (mx < i + P[i])
{
mx = i + P[i];
id = i;
}
}
}