KMP算法 算法摘记
来源:互联网 发布:哈登 nba数据 编辑:程序博客网 时间:2024/05/23 00:06
字符串匹配的KMP算法
1.
首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。
2.
因为B与A不匹配,搜索词再往后移。
3.
就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。
4.
接着比较字符串和搜索词的下一个字符,还是相同。
5.
直到字符串有一个字符,与搜索词对应的字符不相同为止。
6.
这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。
7.
一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。
8.
怎么做到这一点呢?可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)。这张表是如何产生的,后面再介绍,这里只要会用就可以了。
9.
已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。查表可知,最后一个匹配字符B对应的"部分匹配值"为2,因此按照下面的公式算出向后移动的位数:
移动位数 = 已匹配的字符数 - 对应的部分匹配值
因为 6 - 2 等于4,所以将搜索词向后移动4位。
10.
因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2("AB"),对应的"部分匹配值"为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。
11.
因为空格与A不匹配,继续后移一位。
12.
逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。
13.
逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动7位,这里就不再重复了。
14.
下面介绍《部分匹配表》是如何产生的。
首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。
15.
"部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例,
- "A"的前缀和后缀都为空集,共有元素的长度为0;
- "AB"的前缀为[A],后缀为[B],共有元素的长度为0;
- "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;
- "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;
- "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;
- "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;
- "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。
16.
"部分匹配"的实质是,有时候,字符串头部和尾部会有重复。比如,"ABCDAB"之中有两个"AB",那么它的"部分匹配值"就是2("AB"的长度)。搜索词移动的时候,第一个"AB"向后移动4位(字符串长度-部分匹配值),就可以来到第二个"AB"的位置。
void CptPfFunc( ElemType Pattern[], int PrefixFunc[] ) { register int iLen = 0; // Length of Pattern[] while( '\0' != Pattern[iLen] ) iLen++; int LOLP = 0; // Lenth of longest prefix PrefixFunc[1] = 0; for( int NOCM=2; NOCM<iLen+1; NOCM++ ) // NOCM represent the number of characters matched { while( LOLP>0 && (Pattern[LOLP] != Pattern[NOCM-1]) ) LOLP = PrefixFunc[LOLP]; if( Pattern[LOLP] == Pattern[NOCM-1] ) LOLP++; PrefixFunc[NOCM] = LOLP; } }
void KMPstrMatching( ElemType Target[], ElemType Pattern[] ) { int PrefixFunc[MAX_SIZE]; register int TarLen = 0; register int PatLen = 0; // Compute the length of array Target and Pattern 计算长度 while( '\0' != Target[TarLen] ) TarLen++; while( '\0' != Pattern[PatLen] ) PatLen++; // Compute the prefix function of Pattern 计算部分匹配值 CptPfFunc( Pattern, PrefixFunc ); int NOCM = 0; //匹配的长度 for( int i=0; i<TarLen; i++ ) //整个目标串长度 { while( NOCM>0 && Pattern[NOCM] != Target[i] ) // 不匹配 NOCM = PrefixFunc[NOCM]; //更新 当前匹配的为计算得出的部分匹配值 if( Pattern[NOCM] == Target[i] ) //匹配 NOCM++; if( NOCM == PatLen ) //完成匹配 { cout<<"KMP String Matching,pattern occurs with shift "<<i - PatLen + 1<<endl; //输出位置 NOCM = PrefixFunc[NOCM]; } } }
- KMP算法 算法摘记
- 算法分析学习摘记
- 贪心算法 摘记
- B树 算法摘记
- 最大流 算法摘记
- 线段相交 算法摘记
- NP完全性 算法摘记
- 字典树 算法摘记
- 算法摘记 拓扑排序
- 算法摘记 树状数组
- 算法摘记 线段树
- 堆排序 算法摘记
- 快速排序 算法摘记
- RMQ 算法摘记
- 母函数 算法摘记
- 高斯消元法 算法摘记
- 博弈论 算法摘记
- 平衡树 算法摘记
- 9. UINavigationController
- Demo① --> java 学习历程
- windows下快速筛选同一类型文件
- SharePoint开发中的最佳实践
- 问题1——在子进程中调用execl完后,接着调用_exit(0)的意义——< APUE-P201>
- KMP算法 算法摘记
- IaaS, PaaS和SaaS公司都做些什么
- 内存_PA_MAP_VA
- poj 2796 Feel Good (单调栈)
- 统一设计,iOS6也玩扁平化
- 使用jdbc向数据库插入100000条记录
- lvs+ldirectord
- 算法之逆序对问题求解
- HDU 2553 N皇后问题