ブラシLeetCode問題方法Manacherが言及されているとき、それは聞いて作成したアルゴリズムであるため、O(n)との最も長いサブストリングパリンドロームケースの時間計算量で決定することができ、次のチェックに進みますそして、私は、このレコードを見て、効果の下で実現しました。
トピックへのリンク:https://leetcode-cn.com/problems/longest-palindromic-substring/
回文部分文字列:人気のトーク文字列は逆の正のシーケンスであるこれらのasdfdsa、同じ、例えばABA、アバです。
被験者は、その後、我々は、パリンドローム最長ストリング、即ちsdfdsを出力する必要があり、そのような指定された文字列asdfdsabaとして、必要とされます。
この問題を解決するために、このような最も単純なビット列をトラバースすることであり、次いでそれぞれ約延び、最長のパリンドロームストリングの中心としての地位を獲得し、最終的に各回文がサブストリング見つけたように、多くが存在します(拡散の中心)までの最長。これは、例えばABAピット内であり、あなたの周りに拡張、bに行くときアバをサブストリングABAの回文を与えていますが、エラーが生じ、ABBやBBAパリンドロームでない部分文字列を拡張B程度に到達したとき。したがって、このような状況のために、我々は、操作をアピール、その後、各文字列は、このような「#」などの特殊文字、中に挿入されていることを好むすなわちアバは、#Bの#Bは###です。
私たちが言いたい次Manacherアルゴリズムは、私の理解では、上記の方法は、唯一のいくつかの特殊で、場所の最適化は、サブストリングの操作について遡るテキストのすべてのビットを必要としない時間を通過する必要はありませんされ、最適化されていることです場合には大幅に効率を改善し、実行するために行きます。それでは、あなたが部分文字列を必要とするときを探すためには、彼らはこれがManacherアルゴリズムを知ってもらうために等価です知ってもらうとき、何もする必要はありません。
Manacherアルゴリズム
まず、いくつかの概念を理解する必要があります
パリンドロームの中心点:すなわち、パリンドローム配列、## Bは##の中心点B、## Bの#1のBの中心点 ##の第三のため#中心点。
パリンドローム半径:パリンドローム配列の、すなわち、半分の長さではなく、中心点(中心点のいくつかは、それが実際に問題ではない、コードだけの後部の計算に影響を与え、カウントする)、例えば、ABA 1の半径を、アバ半径2
回文は国境:文字の回文文字列である一番左の位置の左端を、それは、右端の文字位置の右側の境界線です。
例えば、現在、文字列qawasdsaz、前記パリンドロームサブAWA、中心点W、添字2,1の半径、添字右3ランドマーク下左マージンを有します。パリンドロームサブストリングasdsa、中心点d、添字5、2の半径、添字3左境界は、右下の境界が7となりました。
実測値:回文右ボーダー= +中心点の半径、パリンドローム左ボーダー=の中心点 - 半径
新しいアレイの半径、すなわち、から構成されて得られる各文字の中心点におけるパリンドロームストリングにおけるパリンドローム半径アレイ。例えば、単に文字列qawasdsaz、添字0、...、qはサブストリングパリンドロームの中心、半径0である中心AWA回文ストリング、半径1のような添え字2、...、添字図5は、このようにして得られ...パリンドロームサブストリングasdsa、2の半径を中心とする半径パリンドローム配列[0、0、1、0、0、2、0、0、0]です。配列内の最大値は、配列の添字の最長の回文サブストリング半径が最も長いサブストリングパリンドロームを得るために、ここでパリンドロームサブ添字列の中心点です。
プロセス
次の最も重要なことは、半径に基づい回文文字列配列を取得する方法である(実際には、中央の拡散方法は、文字列は、それぞれが回文の中心としてそれを計算しなければならないということであり、避けるようにManacherアルゴリズムは、これに基づいて最適化されています多くの計算)。
エラーを回避するためにまず、私たちはそれぞれの前に文字列を横断する操作で特殊記号(例えば「#」)を挿入するには、アバ、このような回文配列を引き起こしました。
すべては、その後、我々は、文字列、レコードの新しい配列トラバース開始2の半径の中心点として、文字の最長の回文文字列を計算radiusArray では。
3.私たちは、その後、いくつかの変数は、データを格納するために使用されているサブストリング現在の回文の中心点を定義センターを、現在の部分文字列の右側の境界回文権利を
4.私たちは、添字iが中心点トラバーサル時間であると仮定センターは = iは、拡散中心の手段によって、iは最長のストリングS、半径rの中心で計算され、即ちradiusArray [ センター ] = R、右側の境界権利は 、I + Rです。
iがjは+後者のトラバーサル5.私は<+ jがあれば、右、また、私はSでjは+ストリングを記載しました。私はスルーJ + 中心 対称の点がIJであり、iは左側のJ ijのを+ので、私たちは、すなわち、パリンドローム既知の半径をijをれるradiusArray [IJ]。我々はまた、サブストリング添字sの左端があることを知っているセンター - radiusArray [ センター ]、パリンドローム対称点ijのサブストリング左マージンIJ- radiusArray [IJ]。我々主張があるradiusArrayの値は[私はjは+]の
上記のデータは、既知の後、我々はいくつかのケースでは、iがj個のパリンドローム半径+と判断され、中央の拡散方法を使用することなく、2つの左の境界線を比較することができます。パリンドローム自然、我々は知ることができ、中心に - radiusArray [ 中央へ] 中央 等しい降順 中心 にセンター + radiusArray [ 中央 ]
- もしIJ-radiusArray [IJ]>中心 -radiusArray [中央]、 すなわち、iがJ +ように両側が、同一であるので、ストリングSにおける対称の左境界点、S文字列が等しいパリンドローム配列パリンドローム配列のIJであります、radiusArray = radiusArray【のIJ] [Iは、Jを+]中央拡散法、中央及び右の定数値の使用を回避します。(私はjは、パリンドロームより長い配列または短くありませんが、回文配列IJ正確に等しく、なぜなら対称のSのとき、内部回文配列、ので+なぜ開始の質問があるかもしれません私はjは長い回文か短い回文構造を+した場合、同時にIJの対応する変化に影響を与えます)
- IJ-radiusArray [IJ] <中心場合 -radiusArray [中央]は、 すなわち、対称サブストリングSの左側の境界点の外側に、今回radiusArrayは[IがJを+] =右- (私はJ + 1) 、中央拡散法は、の使用を回避します同じ値の、中央と右。ストリング(中心radiusArrayのIJ [中央] iがjは右、右==中央+ radiusArray [中央列に等しい+ ]、 しかし中心radiusArray [中央] - 1右+ 1、文字列sの、さもなければ長さに等しくありません)私たちは、さらにしたがって1の半径を大きくします私の回文配列は、右のJストップを+
- IJ-radiusArrayは[IJ] ==中心radiusArray [中央]、サブの左側境界に対称即ち、左境界点は、この時点では判断できない場合はiがjのパリンドローム配列を+、右に停止することがあり、それ以上であってもよいです更新された値の中央と右の間、長い、中心を通って新しい部分の拡散を得る必要があります。(同じ2つの左の境界以来、私たちは右へのi + jはjは+ Iの回文配列の右半分であるかを決定することができますが、長くなることがあります)
6. J + I場合> 右、直接拡散中心の方法によって新たなサブストリングS、すなわち更新得た中央 と右の 値。
見つける7.エンドトラバーサルradiusArrayの半径の最大値、すなわち、文字列の中心点のサブインデックスに対応するインデックス最長サブ回文文字列です。仮定radiusArray [I] Rの最大値、の(「#」を含まない)、元の文字列の部分文字列の添字(IR)/ 2添字の(I + R)/ 2が最長のサブパリンドロームであります弦
回文配列より長く以上がある場合ので、回文配列の右半分は、最も大量の計算の必要性を排除し、直接計算されています。ここで達成するためのコードは次のとおりです。
//求最长的回文子串
public string LongestPalindrome(string s)
{
if (s.Length <= 1)
{
return s;
}
//添加特殊符号 #
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0, len = s.Length; i < len; i++)
{
sb.Append('#');
sb.Append(s[i]);
}
sb.Append('#');
string newString = sb.ToString();
sb.Clear();
sb = null;
//回文半径数组
int[] radiusArray = new int[newString.Length];
//center当前回文串中心点,right当前回文串右边界,left当前回文串左边界,symmetryIndex当前遍历下标的对称下标,symmetryIndexLeft对称下标的回文串左边界
int center = -1, right = -1, left = -1, symmetryIndex = -1, symmetryIndexLeft = -1;
//maxRadius 最大半径的值,maxIndex 最大半径的下标
int maxRadius = -1, maxIndex = -1;
//遍历,如果最长回文串的半径>剩下的长度,则返回。最后两个字符可以不用遍历,因为其回文串肯定是本身
for (int i = 0, len = newString.Length; i < len - 2 && maxRadius < (len - i); i++)
{
if (i > right)
{
right = GetRight(newString, i);
center = i;
radiusArray[i] = right - i;
}
else
{
symmetryIndex = center - (i - center);
symmetryIndexLeft = symmetryIndex - radiusArray[symmetryIndex];
left = center - (right - center);
if (symmetryIndexLeft > left)
{
radiusArray[i] = radiusArray[symmetryIndex];
}
else if (symmetryIndexLeft < left)
{
radiusArray[i] = right - i;
}
else
{
right = GetRight(newString, i);
center = i;
radiusArray[i] = right - i;
}
}
//纪录最大值
if (radiusArray[i]> maxRadius)
{
maxRadius = radiusArray[i];
maxIndex = i;
}
}
return s.Substring((maxIndex - maxRadius) / 2, maxRadius);
}
//中心扩散 获取某个下标的回文串右边界
public int GetRight(string s, int index)
{
int right = index;
for(int i=1,len = s.Length; index + i < len && index - i >= 0; i++)
{
if (s[index - i] != s[index + i])
{
return right;
}
else
{
right = index + i;
}
}
return right;
から提出されました