我的理解:总的来说,由于传统的暴力求字符串匹配复杂度为0(n*m),效率太低。然后出现了KMP算法,效率 0(m+n).其算法的核心在于求解最长前缀后得到要跳过的距离(即不需要去匹配的串直接跳过)
题目:
Description
Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis surgissait l’inhumain, l’affolant. Il aurait voulu savoir où s’articulait l’association qui l’unissait au roman : stir son tapis, assaillant à tout instant son imagination, l’intuition d’un tabou, la vision d’un mal obscur, d’un quoi vacant, d’un non-dit : la vision, l’avision d’un oubli commandant tout, où s’abolissait la raison : tout avait l’air normal mais…
Perec would probably have scored high (or rather, low) in the following contest. People are asked to write a perhaps even meaningful text on some subject with as few occurrences of a given “word” as possible. Our task is to provide the jury with a program that counts these occurrences, in order to obtain a ranking of the competitors. These competitors often write very long texts with nonsense meaning; a sequence of 500,000 consecutive 'T's is not unusual. And they never use spaces.
So we want to quickly find out how often a word, i.e., a given string, occurs in a text. More formally: given the alphabet {'A', 'B', 'C', …, 'Z'} and two finite strings over that alphabet, a word W and a text T, count the number of occurrences of W in T. All the consecutive characters of W must exactly match consecutive characters of T. Occurrences may overlap.
Input
One line with the word W, a string over {'A', 'B', 'C', …, 'Z'}, with 1 ≤ |W| ≤ 10,000 (here |W| denotes the length of the string W).
One line with the text T, a string over {'A', 'B', 'C', …, 'Z'}, with |W| ≤ |T| ≤ 1,000,000.
Output
Sample Input
3 BAPC BAPC AZA AZAZAZA VERDI AVERDXIVYERDIAN
Sample Output
1 3 0
题目描述:求解字符串W在T中出现的次数
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
using namespace std;
typedef pair<int, int> P;
#define LOCAL
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define MAX_N 10000
char target[1000005];
char Pattern[10005];
int PrefixFunc[10005];//前缀数组
//求模式串Pattern的前缀函数,位移为 dex - PrefixFunc
void next()
{
int PatLen = strlen(Pattern);
int LOLP = 0;//最长前缀长度初始化为0
PrefixFunc[1] = 0;//匹配一个就失配的情况
for(int NOCM = 2; NOCM < PatLen + 1; NOCM++)//NOCM代表匹配的字符数目(1——12)如果模式串为12个字符
{
while(LOLP > 0 && (Pattern[LOLP] != Pattern[NOCM - 1]))
LOLP = PrefixFunc[LOLP];
if(Pattern[LOLP] == Pattern[NOCM-1])//更新最长前缀的长度
LOLP++;
PrefixFunc[NOCM] = LOLP;
}
}
void KMP()
{
int ans = 0;
int tar_length = strlen(target);
int pat_length = strlen(Pattern);
next();
int NOCM = 0;//匹配字符的数量
for(int i = 0; i < tar_length; i++)
{
while(NOCM > 0 && Pattern[NOCM] != target[i])
NOCM = PrefixFunc[NOCM];
if(Pattern[NOCM] == target[i])
NOCM++;
if(NOCM == pat_length)
{
ans++;
// cout << "子串位置" << i - pat_length + 1 << endl;
NOCM = PrefixFunc[NOCM];
}
}
printf("%d\n", ans);
}
int main()
{
#ifdef LOCAL
freopen("b:\\data.in.txt", "r", stdin);
#endif
int T;
scanf("%d", &T);
while(T--)
{
memset(PrefixFunc, 0, sizeof(PrefixFunc));//清空前缀数组
scanf("%s%s", Pattern, target);
KMP();
}
return 0;
}
相信很多人(包括自己)初识KMP算法的时候始终是丈二和尚摸不着头脑,要么完全不知所云,要么看不懂书上的解释,要么自己觉得好像心里了解KMP算法的意思,却说不出个究竟,所谓知其然不知其所以然是也。
-----------------------------------谨以此文,献给刚接触KMP算法的朋友,定有不足之处,望大家指正----------------------------------------
【KMP算法简介】
【传统字符串匹配算法的缺憾】
请看以下传统字符串匹配的代码:
void NativeStrMatching( ElemType Target[], ElemType Pattern[] )
{
}
【代码思想】
不妨假设我们的目标串
Target =
需要匹配的模式串
Pattern = "c d f";
那么当匹配到如下情况时
由于 'e' != 'f' ,因此失配,那么下次匹配起始位置就是目标串的'd'字符
我们发现这里照样失配,直到运行到下述情况
也就是说,中间的四个字符 d e a b 完全没有必要检测,直接跳转到下一个'c'开始的地方进行检测
【KMP算法的引入】
【KMP算法思想详述与实现】
下面分为两个板块分别详述:
【前缀函数的引入及实现】
【前缀函数的引入】
理解了什么是前、后缀,就来看看什么是前缀函数:
【前缀函数的实现】
下面就来分析怎么用代码来表达这种关系。
这里采用《算法导论(第二版)》中的思想求解。
不妨以 PrefixFunc[] 表示这个前缀函数,那么我们将得到以下求前缀函数的函数:
由于 0 个匹配字符数在计算中没有意义,因此PrefixFunc下标从1开始,也就是从已经有一个字符(即首字符)匹配的情况开始
// Compute Prefix function
void CptPfFunc( ElemType Pattern[], int PrefixFunc[] )
{
}
NOCM 表示 已经匹配的字符数
LOLP 表示 既是自身真后缀又是自身最长前缀的字符串长度
以下是计算流程:
PrefixFunc[1] = 0; //只匹配一个字符就失配时,显然该值为零
LOLP = 0;
LOLP = 0;
LOLP = 0;
LOLP = 0;
LOLP = 1;
LOLP = 2;
LOLP = 3;
LOLP = 4;
LOLP = 5;
LOLP = 6;
LOLP = 7;
---------此时满足条件while( LOLP>0 && (Pattern[LOLP] != Pattern[NOCM-1]) )-------------
while语句中的执行
{
}
LOLP = 0;
最后我们的前缀函数 PrefixFunc[] = { 0,0,0,0,1,2,3,4,5,6,7,1 }
其间最精妙的要属失配时的操作
while( LOLP>0 && (Pattern[LOLP] != Pattern[NOCM-1]) )
其中 LOLP = PrefixFunc[LOLP];
由以上分析,不难推导KMP算法的实现
void KMPstrMatching( ElemType Target[], ElemType Pattern[] )
{
}
【参考文献】
《Introduction to Algorithms》Second Edition
by Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford
转自