#region KMP generic
private static int[] Next(IList<T> pattern)
{
int[] next = new int[pattern.Count];
next[0] = -1;
if (pattern.Count < 2) //如果只有1個元素不用kmp效率會好一些
{
return next;
}
next[1] = 0; //第二個元素的回溯函數值必然是0,可以證明:
//1的前置序列集為{空集,L[0]},L[0]的長度不小於1,所以淘汰,空集的長度為0,故回溯函數值為0
int i = 2; //正被計算next值的字元的索引
int j = 0; //計算next值所需要的中間變數,每一輪迭代初始時j總為next[i-1]
while (i < pattern.Count) //很明顯當i==pattern.Length時所有字元的next值都已計算完畢,任務已經完成
{ //狀態點
//用Equals作為元素匹配條件
if (pattern[i - 1].Equals(pattern[j])) //首先必須記住在本函數實現中,迭代計算next值是從第三個元素開始的
{ //如果L[i-1]等於L[j],那麼next[i] = j + 1
next[i++] = ++j;
}
else
{ //如果不相等則檢查next[i]的下一個可能值----next[j]
j = next[j];
if (j == -1) //如果j == -1則表示next[i]的值是1
{ //可以把這一部分提取出來與外層判斷合并
//書上的kmp代碼很難理解的一個原因就是已經被最佳化,從而遮蔽了其實際邏輯
next[i++] = ++j;
}
}
}
return next;
}
public static int ExecuteKMP(IEnumerable<T> source, IList<T> pattern)
{
int[] next = Next(pattern);
return ExecuteKMPInternal(source, pattern, next);
}
private static int ExecuteKMPInternal(IEnumerable<T> source, IList<T> pattern, int[] next)
{
IEnumerator<T> iterator = source.GetEnumerator();
int i = iterator.MoveNext() ? 0 : -1;//這兩條語句必須總是一起執行 //主串指標
int j = 0; //模式串指標
//如果子串沒有匹配完畢並且主串沒有搜尋完成
while (j < pattern.Count && i > -1)
{
if (iterator.Current.Equals(pattern[j])) //i和j的邏輯意義體現於此,用於指示本輪迭代中要判斷是否相等的主串字元和模式串字元
{
i = iterator.MoveNext() ? i + 1 : -1;
j++;
}
else
{
j = next[j]; //依照指示迭代回溯
if (j == -1) //回溯有情況,這是第二種
{
i = iterator.MoveNext() ? i + 1 : -1;
j++;
}
}
}
//如果j==pattern.Length則表示迴圈的退出是由於子串已經匹配完畢而不是主串用盡
return j < pattern.Count ? -1 : i - j;
}
/// <summary>
/// 泛型版的Next函數
/// </summary>
/// <param name="pattern">模式串可以是一個實現了IList的對象,所有數組都實現了IList</param>
/// <param name="isEqual">此函數必須是反映一個等價關係,即滿足自反、傳遞、交換,否則演算法會出現邏輯錯誤。這是KMP演算法的前提。</param>
/// <returns>返回Next回溯函數</returns>
private static int[] Next(IList<T> pattern, Func<T, T, bool> isEqual)
{
int[] next = new int[pattern.Count];
next[0] = -1;
if (pattern.Count < 2) //如果只有1個元素不用kmp效率會好一些
{
return next;
}
next[1] = 0; //第二個元素的回溯函數值必然是0,可以證明:
//1的前置序列集為{空集,L[0]},L[0]的長度不小於1,所以淘汰,空集的長度為0,故回溯函數值為0
int i = 2; //正被計算next值的字元的索引
int j = 0; //計算next值所需要的中間變數,每一輪迭代初始時j總為next[i-1]
while (i < pattern.Count) //很明顯當i==pattern.Length時所有字元的next值都已計算完畢,任務已經完成
{ //狀態點
//用Equals作為元素匹配條件
if (isEqual(pattern[i - 1], pattern[j])) //首先必須記住在本函數實現中,迭代計算next值是從第三個元素開始的
{ //如果L[i-1]等於L[j],那麼next[i] = j + 1
next[i++] = ++j;
}
else
{ //如果不相等則檢查next[i]的下一個可能值----next[j]
j = next[j];
if (j == -1) //如果j == -1則表示next[i]的值是1
{ //可以把這一部分提取出來與外層判斷合并
//書上的kmp代碼很難理解的一個原因就是已經被最佳化,從而遮蔽了其實際邏輯
next[i++] = ++j;
}
}
}
return next;
}
public static int ExecuteKMP(IEnumerable<T> source, IList<T> pattern, Func<T, T, bool> isEqual)
{
int[] next = Next(pattern, isEqual);
return ExecuteKMPInternal(source, pattern, isEqual, next);
}
private static int ExecuteKMPInternal(IEnumerable<T> source, IList<T> pattern, Func<T, T, bool> isEqual, int[] next)
{
IEnumerator<T> iterator = source.GetEnumerator();
int i = iterator.MoveNext() ? 0 : -1;//這兩條語句必須總是一起執行 //主串指標
int j = 0; //模式串指標
//如果子串沒有匹配完畢並且主串沒有搜尋完成
while (j < pattern.Count && i > -1)
{
if (isEqual(iterator.Current, pattern[j])) //i和j的邏輯意義體現於此,用於指示本輪迭代中要判斷是否相等的主串字元和模式串字元
{
i = iterator.MoveNext() ? i + 1 : -1;
j++;
}
else
{
j = next[j]; //依照指示迭代回溯
if (j == -1) //回溯有情況,這是第二種
{
i = iterator.MoveNext() ? i + 1 : -1;
j++;
}
}
}
//如果j==pattern.Length則表示迴圈的退出是由於子串已經匹配完畢而不是主串用盡
return j < pattern.Count ? -1 : i - j;
}
private static int[] NextVal(IList<T> pattern)
{
int[] next = new int[pattern.Count];
next[0] = -1;
if (pattern.Count < 2) //如果只有1個元素不用kmp效率會好一些
{
return next;
}
next[1] = 0; //第二個元素的回溯函數值必然是0,可以證明:
//1的前置序列集為{空集,L[0]},L[0]的長度不小於1,所以淘汰,空集的長度為0,故回溯函數值為0
int i = 2; //正被計算next值的字元的索引
int j = 0; //計算next值所需要的中間變數,每一輪迭代初始時j總為next[i-1]
while (i < pattern.Count) //很明顯當i==pattern.Length時所有字元的next值都已計算完畢,任務已經完成
{ //狀態點
//用Equals作為元素匹配條件
if (j == -1 || pattern[i - 1].Equals(pattern[j])) //首先必須記住在本函數實現中,迭代計算next值是從第三個元素開始的
{ //如果L[i-1]等於L[j],那麼next[i] = j + 1
j++;
if (pattern[i].Equals(pattern[j]))
{
next[i] = next[j];
}
else
{
next[i] = j;
}
i++;
}
else
{ //如果不相等則檢查next[i]的下一個可能值----next[j]
j = next[j];
}
}
return next;
}
public static int ExecuteKMPP(IEnumerable<T> source, IList<T> pattern)
{
int[] next = NextVal(pattern);
return ExecuteKMPInternal(source, pattern, next);
}
#endregion
剛才測試一下,貌似沒有問題。