一、問題描述
現給定一個已知的字符串str[],現在想要在O(n)的時間複雜度之內求出一個最長的迴文子字符串(正着和倒着順序讀一致)。
Manacher最早發現了可以用O(n)的時間複雜度來解決該問題,所以這種方法稱之爲Manacher算法。
P[]存放的是迴文子串的半徑
Manacher算法精髓就是對稱的思想,
假設i是我們將要計算迴文串半徑的字符,就是下一步我們要計算P[i]。
我們已知:
1,以id爲中心,P[id]爲半徑的範圍正好可以覆蓋i; if (id+P[id]>i)
2, 以id爲對稱中心,i的對稱點是j,P[j]我們正好也知道。
可以得到:
P[i]>=min(P[id]-(id-j), P[j]);
原因:如果j的半徑較小,那麼i的半徑必須等於P[j],(如果i的半徑比j大,又因爲i和j關於id對稱,兩者就矛盾了,所以i的半徑在這種情況下只能等於j的半徑);如下圖由對稱性1和2相等,2和3相等,3和4相等——》p[j]>p[j] 矛盾! 所以i的半徑必須等於P[j]!
如果j的半徑較大,那麼i的半徑只能等於P[id]-(id-j)(假設i的半徑大於P[id]-(id-j),由於對稱性,則j的半徑要大於P[j],這也於已知矛盾。)如下圖:如下圖由對稱性1和2相等,2和3相等,3和4相等——》p[id]>p[id] 矛盾! 所以i的半徑必須等於P[id]-(id-j)!
除此之外,就是P[id]爲半徑的範圍沒有覆蓋i;
這種情況就只能i以自己爲中心計算半徑了。
和上面圖像有些不同,代碼裏的對稱中心是j,所以以j爲中心i的對稱點就是2 * j - i,
for (int i = 1; i < len; i++)
{
if (j+P[j]>i) //這種情況,j才能作爲i的對稱中心來簡化計算,算法的精髓在P[i] >=min(P[2 * j - i],j+P[j]-i);
{
P[i] = min(P[2 * j - i],j+P[j]-i);
}
else //這種情況,p[i]要重新計算,
{
P[i] = 1;
}
while (newstr[i + P[i]] == newstr[i - P[i]] && i + P[i] <= len)//以i爲對稱中心,看i兩邊的元素是否相等,更新p[i]
{
P[i]++;
}
if ((j + P[j]) < i + P[i])
j = i;
if (P[i]>maxlength)//maxlength用來統計最長的迴文串長度
maxlength = P[i];
}
return;
}