一、BF算法
最简单直观的模式匹配算法是BF(Brute-Fore)算法.
[算法思想]
从主串S的第pos个字符起和模式的第一个字符进行比较,若相等,则进行逐个比较后续字符;否则从主串的下一个字符起再重新和模式的字符比较.
依次类推,直至模式T中的每个字符依次和主串S中的一个连续的字符序列相等,则匹配成功,函数返回值为和模式T中第一个字符相等的字符在主串S中的序号,
否则称匹配不成功,函数返回值为零
[算法描述]
int Index(SString S,SString T,int pos)
{
//返回模式T在主串S中第pos个字符开始第一次出现的位置.若不存在,则返回值为0
//其中,T非空,1<=pos<=StrLength(S)
i=pos; j=1;
while(i<=S[0] && j<=T[0])/* T[0]、S[0]分别用来存储字符串T的长度和S的长度 */
{
if(S[i] == T[i]) //继续比较后继字符
{
++i;
++j;
}
else //指针后退重新开始匹配
{
i=i-j+2; //主串的下一个字符起再重新和模式的字符比较
j=1;
}
}
if(j>T[0])
{
return i-T[0];
}
else
{
return 0;
}
}
1)最好情况下的平均时间复杂度是O(n+m)
2)最坏情况下的平均时间复杂度是O(n*m)
来看一个例子:
[问题描述]
1204 寻找子串位置
时间限制: 1 s 空间限制: 128000 KB 题目等级 : 青铜 Bronze
题目描述 Description
给出字符串a和字符串b,保证b是a的一个子串,请你输出b在a中第一次出现的位置。
输入描述 Input Description
仅一行包含两个字符串a和b
输出描述 Output Description
仅一行一个整数
样例输入 Sample Input
abcd bc
样例输出 Sample Output
2
数据范围及提示 Data Size & Hint
字符串的长度均不超过100
[代码实现]
#include<iostream>
#include<string.h>
using namespace std;
int Index(const string S,const string T)
{
int i=0; int j=0;
while(S[i]&&T[j])
{
if(S[i] == T[j]) //继续比较后继字符
{
++i;
++j;
}
else //指针后退重新开始匹配
{
i=i-j+1; //主串的下一个字符起再重新和模式的字符比较
j=0;
}
}
int TLenth=T.length()-1;
if(j>TLenth )
{
return i-TLenth;
}
return 0;
}
int main()
{
string a,b;
cin>>a>>b;
cout<<Index(a,b);
return 0;
}
二、KMP算法
kmp算法的原理,即求出P0...Pi的最大
1.kmp算法的原理
字符串匹配是计算机的基本任务之一.
举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"
详见(我上传的KMP算法.doc)
2.next数组的求解思路
通过上文完全可以对kmp算法的原理有个清晰的了解,那么下一步就是编程实现了,其中最重要的就是如何根据待匹配的模板字符串
求出对应每一位的最大相同前后缀的长度.
void makeNext(const char P[],int next[])现在着重讲解一下while循环所做的工作:
{
int q,k; /*q:模板字符串下标; k:最大前后缀长度*/
int m=strlen(P); /*模板字符串长度*/
next[0]=0;
for(q=1,k=0;q<m;++q) /*for循环,从第二个字符开始,依次计算每一个字符对应的next值*/
{
while(k>0 && P[q] != P[k]) /*递归的求出P[0]...P[q]的最大的相同的前后缀长度k*/
{
k=next[k-1]; /*不理解没关系看下面的分析,这个while循环是整段代码的精髓所在,确实不好理解*/
}
if(P[q]==P[k]) /*如果相等,那么最大相同前后缀长度加1*/
{
k++;
}
next[q]=k;
}
}
1.已知前一步计算时最大相同的前后缀长度为k(k>0),即P[0]...P[k-1];
2.此时比较第k项P[k]与P[q],如图1所示
3.如果P[k]等于P[q],那么简单跳出while循环;
4.关键!如果不等,那么我们应该利用已经得到的next[0]..next[k-1]来求P[0]...P[k-1]这个子串中最大相同前后缀.
为什么要求P[0]...P[k-1]的最大相同前后缀呢? 原因在于P[k]已经和P[q]失配了,而且P[q-k]...P[q-1]又与P[0]...P[k-1]相同,
看来P[0]...P[k-1]怎么唱的子串是用不了了,那么要找个同样也是P[0]打头、P[k-1]结尾的子串即P[0]..P[j-1](j==next[k-1]),
看看它的下一项P[j]是否能和P[q]匹配.如图2所示
#include<stdio.h>
#include<string.h>
void makeNext(const char P[],int next[])
{
int q,k;
int m=strlen(P);
next[0]=0;
for(q=1,k=0;q<m;++q)
{
while(k>0 && P[q]!= P[k])
{
k=next[k-1];
}
if(P[q]==P[k])
{
k++;
}
next[q]=k;
}
}
int kmp(const char T[], const char P[],int next[])
{
int n,m;
int i,q;
n=strlen(T);
m=strlen(P);
makeNext(P,next);
for(i=0,q=0;i<n;++i)
{
while(q>0 &&P[q]!=T[i])
{
q=next[q-1];
}
if(P[q] == T[i])
{
q++;
}
if(q==m)
{
printf("Pattern occurs with shift:%d\n",(i-m+1));
}
}
}
int main()
{
int i;
int next[20]={0};
char T[]="ababxbababcadfdsss";
char P[]="abcdabd";
printf("%s\n",T);
printf("%s\n",P);
kmp(T,P,next);
for(i=0;i<strlen(P);++i)
{
printf("%d ",next[i]);
}
printf("\n");
return 0;
}