Total KMP

Because it was a nightmare.

ContractedBlock.gif ExpandedBlockStart.gif Code
namespace ConsoleTest
{
    
class KMPTest
    {
        
public static void Test()
        {
            
int[] source;
            
int[] pattern;

            
//source = new[] { 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1 };
            
//pattern = new[] { 4, 3, 2 };

            
//source = new[] { 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1 };
            
//pattern = new[] { 4, 3, 2, 2 };

            
//source = new[] { 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1 };
            
//pattern = new[] { 1, 2, 3 };

            
//source = new[] { 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1 };
            
//pattern = new[] { 6, 7, 6 };

            source 
= new[] { 112111221 };
            pattern 
= new[] { 1112 };

            
//source = new[] {1,2,3,1,1,1,2,3,4 };
            
//pattern = new[] {1,1,1 };

            
//source = new[] { 1,1,2,2,3};
            
//pattern = new[] { 1,2 };

            
//source = new[] {1,1,2,2,3,3,3};
            
//pattern = new[] { 5,5,5 };

            
//source = new[] { 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1 };
            
//pattern = new[] { 4, 3, 2 };

            Console.WriteLine(IndexOf
<int>.ExecuteKMPP(source, pattern));



        }
    }
}

 

 

 

ContractedBlock.gif ExpandedBlockStart.gif Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ZhyCodeLibrary.Algo
{
    
/// <summary>
    
/// System.String类实现了IEnumerable&lt;Char&gt;,但是没有实现IList&lt;Char&gt;
    
/// 所以应该另外实现针对String的KMP算法
    
/// 
    
/// </summary>
    
/// <typeparam name="T"></typeparam>
    public static class IndexOf<T>
    {
        
#region KMP generic

        
/// <summary>
        
/// 泛型版的Next回溯函数求解方法
        
/// </summary>
        
/// <param name="pattern">模式串</param>
        
/// <returns>Next回溯函数</returns>
        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);
        }

        
/// <summary>
        
/// KMP的核心包括两部分,Next(NextVal)求解和KMP匹配函数
        
/// 本方法实现了泛型版的KMP匹配函数
        
/// ExcuteKMP方法是对KMP调用的便捷封装
        
/// </summary>
        
/// <param name="source"></param>
        
/// <param name="pattern"></param>
        
/// <param name="isEqual">必须是等价函数,这是KMP函数的前提,切记。</param>
        
/// <param name="next"></param>
        
/// <returns></returns>
        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;
        }

        
/// <summary>
        
/// 终于把KMP算法搞定了,两眼全是泪水
        
/// </summary>
        
/// <param name="pattern"></param>
        
/// <returns></returns>
        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;
        }

        
//KMPP=KMP plus
        public static int ExecuteKMPP(IEnumerable<T> source, IList<T> pattern)
        {
            
int[] next = NextVal(pattern);
            
return ExecuteKMPInternal(source, pattern, next);
        }

        
#endregion

        
#region KMP string

        
/// <summary>
        
/// 求一个字符串的回溯函数。
        
/// 约定序列下标从0开始。
        
/// 回溯函数是整数集[0,n-1]到N的映射,n为字符串的长度。
        
/// 回溯函数的定义:
        
/// 设存在非空序列L,i为其合法下标;
        
/// L[i]的前前置序列集为:{空集,L中所有以i-1为最后一个元素下标的子序列}
        
/// L的前置序列集为:{空集,L中所有以0为第一个元素下标的子序列}
        
/// 下标i的回溯函数值的定义为:
        
/// 如果i=0,回溯函数值为-1
        
/// 否则i的回溯函数值为i的前置序列集和L的前置序列集中相等元素的最大长度
        
/// 换句话说是,设集合V={x,x属于i的前置序列集,并且x属于L的前置序列集,并且x的长度小于i},回溯函数值为max(V)
        
/// 当i=0时并不存在这样的一个x,所以约定此时的回溯函数值为-1
        
/// 回溯函数的意义:
        
/// 如果标号为j的字符同主串失配,那么将子串回溯到next[j]继续与主串匹配,如果next[j]=-1,则主串的匹配点后移一位,同子串的第一个元素开始匹配。
        
/// 同一般的模式匹配算法相比,kmp通过回溯函数在失配的情况下跳过了若干轮匹配(向右滑动距离可能大于1)
        
/// kmp算法保证跳过去的这些轮匹配一定是失配的,这一点可以证明
        
/// </summary>
        
/// <param name="pattern">模式串,上面的注释里将其称为子串</param>
        
/// <returns>回溯函数是kmp算法的核心,本函数依照其定义求出回溯函数,KMP函数依照其意义使用回溯函数。</returns>
        private static int[] Next(string pattern)
        {
            
int[] next = new int[pattern.Length];
            next[
0= -1;
            
if (pattern.Length < 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.Length)    //很明显当i==pattern.Length时所有字符的next值都已计算完毕,任务已经完成
            { //状态点
                if (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;
        }

        
/// <summary>
        
/// KMP函数同普通的模式匹配函数的差别在于使用了next函数来使模式串一次向右滑动多位称为可能
        
/// next函数的本质是提取重复的计算
        
/// </summary>
        
/// <param name="source">主串</param>
        
/// <param name="pattern">用于查找主串中一个位置的模式串</param>
        
/// <returns>-1表示没有匹配,否则返回匹配的标号</returns>
        public static int ExecuteKMP(string source, string pattern)
        {
            
int[] next = Next(pattern);
            
return ExecuteKMPInternal(source, pattern, next);
        }

        
private static int ExecuteKMPInternal(string source, string pattern, int[] next)
        {
            
int i = 0;  //主串指针
            int j = 0;  //模式串指针
            
//如果子串没有匹配完毕并且主串没有搜索完成
            while (j < pattern.Length && i < source.Length)
            {
                
if (source[i] == pattern[j])    //i和j的逻辑意义体现于此,用于指示本轮迭代中要判断是否相等的主串字符和模式串字符
                {
                    i
++;
                    j
++;
                }
                
else
                {
                    j 
= next[j];    //依照指示迭代回溯
                    if (j == -1)    //回溯有情况,这是第二种
                    {
                        i
++;
                        j
++;
                    }
                }
            }
            
//如果j==pattern.Length则表示循环的退出是由于子串已经匹配完毕而不是主串用尽
            return j < pattern.Length ? -1 : i - j;
        }

        
#endregion

        
#region KMP plus string

        
/// <summary>
        
/// NextVal是另外一种求回溯函数的方法,同Next基本上一样
        
/// 唯一的区别在于j指针回溯的时候,判断一下pattern[j]同pattern[i]是否相等,如果相等,由于是在i处失配,
        
/// 那么与在j处也必然失配,所以直接回溯到Next[j]
        
/// 在next中,判断回溯后的串是否匹配完全是KMP的责任,但在NextVal中将一部分判断提取到了NextVal中
        
/// KMP实在是太复杂了,感觉没有说得特别清楚。估计以后再捡起来还要费不少劲。
        
/// so keep thinking
        
/// </summary>
        
/// <param name="pattern">模式串</param>
        
/// <returns>NextVal回溯函数</returns>
        public static int[] NextVal(string pattern)
        {
            
int[] next = new int[pattern.Length];
            next[
0= -1;
            
if (pattern.Length < 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.Length)    //很明显当i==pattern.Length时所有字符的next值都已计算完毕,任务已经完成
            { //状态点
                if (j == -1 || pattern[i - 1== pattern[j])   //首先必须记住在本函数实现中,迭代计算next值是从第三个元素开始的
                {   //如果L[i-1]等于L[j],那么next[i] = j + 1
                    ++j;
                    
if (pattern[i] == pattern[j])
                    {
                        next[i] 
= next[j];  //超量回溯
                    }
                    
else
                    {
                        next[i] 
= j;    //标准的next回溯
                    }
                    i
++;

                }
                
else
                {   
//如果不相等则检查next[i]的下一个可能值----next[j]
                    j = next[j];

                }
            }
            
return next;
        }

        
/// <summary>
        
/// KMPP means KMP plus
        
/// </summary>
        
/// <param name="source"></param>
        
/// <param name="pattern"></param>
        
/// <returns>-1 means not found</returns>
        public static int ExecuteKMPP(string source, string pattern)
        {
            
int[] next = NextVal(pattern);
            
int i = 0;  //主串指针
            int j = 0;  //模式串指针
            
//如果子串没有匹配完毕并且主串没有搜索完成
            while (j < pattern.Length && i < source.Length)
            {
                
if (source[i] == pattern[j])    //i和j的逻辑意义体现于此,用于指示本轮迭代中要判断是否相等的主串字符和模式串字符
                {
                    i
++;
                    j
++;
                }
                
else
                {
                    j 
= next[j];    //依照指示迭代回溯
                    if (j == -1)    //回溯有情况,这是第二种
                    {
                        i
++;
                        j
++;
                    }
                }
            }
            
//如果j==pattern.Length则表示循环的退出是由于子串已经匹配完毕而不是主串用尽
            return j < pattern.Length ? -1 : i - j;
        }

        
#endregion

        
public static int Execute(IEnumerable<T> list, T elem, Func<T, T, bool> isEqual)
        {
            
int i = 0;
            
foreach (T t in list)
            {
                
if (isEqual(t, elem))
                {
                    
return i;
                }
                i
++;
            }
            
return -1;
        }
    }
}

 

测试代码:

 

 

转载于:https://www.cnblogs.com/zhy2002/archive/2008/12/18/1357655.html

基于SSM框架的智能家政保洁预约系统,是一个旨在提高家政保洁服务预约效率和管理水平的平台。该系统通过集成现代信息技术,为家政公司、家政服务人员和消费者提供了一个便捷的在线预约和管理系统。 系统的主要功能包括: 1. **用户管理**:允许消费者注册、登录,并管理他们的个人资料和预约历史。 2. **家政人员管理**:家政服务人员可以注册并更新自己的个人信息、服务类别和服务时间。 3. **服务预约**:消费者可以浏览不同的家政服务选项,选择合适的服务人员,并在线预约服务。 4. **订单管理**:系统支持订单的创建、跟踪和管理,包括订单的确认、完成和评价。 5. **评价系统**:消费者可以在家政服务完成后对服务进行评价,帮助提高服务质量和透明度。 6. **后台管理**:管理员可以管理用户、家政人员信息、服务类别、预约订单以及处理用户反馈。 系统采用Java语言开发,使用MySQL数据库进行数据存储,通过B/S架构实现用户与服务的在线交互。系统设计考虑了不同用户角色的需求,包括管理员、家政服务人员和普通用户,每个角色都有相应的权限和功能。此外,系统还采用了软件组件化、精化体系结构、分离逻辑和数据等方法,以便于未来的系统升级和维护。 智能家政保洁预约系统通过提供一个集中的平台,不仅方便了消费者的预约和管理,也为家政服务人员提供了一个展示和推广自己服务的机会。同时,系统的后台管理功能为家政公司提供了强大的数据支持和决策辅助,有助于提高服务质量和管理效率。该系统的设计与实现,标志着家政保洁服务向现代化和网络化的转型,为管理决策和控制提供保障,是行业发展中的重要里程碑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值