活到老学到老

现学现卖

博客园 首页 新随笔 联系 订阅 管理

        #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

刚才测试一下,貌似没有问题。

posted on 2008-04-01 13:08  John Rambo  阅读(1059)  评论(1编辑  收藏  举报