C#版泛型kmp演算法

來源:互聯網
上載者:User

        #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

剛才測試一下,貌似沒有問題。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.