KMP算法
在一个长的字符串中查找子串位置,通过暴力遍历肯定能写出这个匹配字符串的方法,但是效率太过低下。所以有了KMP算法来解决这个问题。
具体KMP的算法概念和理解我是参考了这两篇博客: https://www.cnblogs.com/yjiyjige/p/3263858.html
https://blog.youkuaiyun.com/x__1998/article/details/79951598
这两篇博客概念比较清楚了,但是程序拿来用会有些问题,我主要是想写一下关于用c++实现这个算法的具体实现,以及遇到的问题。虽然大部分是比较低级错误,写出来还是希望和我一样的新手在遇到这些问题少走点弯路,也为了让自己加深影响和理解。
KMP算法就是在长的字符串中查找子串的位置,找到就返回其位置下标,未找到就返回-1.具体算法实现就是:先对模式串进行一个next数组计算,计算遇到不匹配时模式串应该跳转到什么位置开始循环(具体理解参考我前面给的博客)。有了这个next数组后,进行KMP算法函数实现,找到就返回其位置下标,未找到就返回-1.这样在查找过程中主串的位置i就不必回溯,节约时间。
程序实现
// 程序头定义
#include<iostream>
#include<string>
using namespace std;
注:从c编程转到c++,尽量用<iostream>替换掉<stdio.h>头文件,还有<string.h>和<string>是一样的。
next函数定义:该函数作用就是, 获得模式串在遇到不匹配的情况,子串的各个位置的应该跳转到哪一个子串位置j开始查找。说明一下,next[0]=-1是为了程序编程方便而给的,在大话数据结构书中是从next[0]=0开始的更好理解,但是编程不方便。
// next数组计算
void get_next(string &T,int *next)
{ int i,j;
i=0; //i是主串位置,j是模式串位置
j=-1;
next[0]=-1;
int tmp=T.size(); //成员函数size和length获得的是无符号型整数
while(i<tmp)
{
if (j==-1 || T[i]==T[j]) //T[i]是后缀单个字符,T[j]是前缀单个字符
next[++i]=++j;
else
j=next[j];
} //我在这里输出了next数组值
cout<<next[0]<<next[1]<<next[2]<<next[3]<<next[4]<<next[5]<<endl;
}
// KMP函数定义
int KMP(string &x,string &T)
{
int *next=new int[100];
int i=0,j=0;
get_next(T,next);
int tmp1=T.size(),tmp2=x.size();
while( i<tmp2 && j<tmp1)
{
if (j==-1 || x[i]==T[j])
{
i++;
j++;
}
else
j=next[j];
}
delete[] next;
if (j==tmp1)
return i-j;
else
return -1;
}
注:我在这里遇到几个问题:(1)是运用string成员函数strlen()、size()和length(),首先strlen(a),这里a必须是char型数组,我这里定义的是string故不行,而size()和length()返回的是unsigned int型,如果放在while(i<T.size())中比较,i是有符号型与无符号型比较会出问题,这个问题我找了好久才找到,略微有点low但是花了我好长时间。
(2)int *next=new int[10];创建一个动态数组,这里的动态数组,动态是指内存可收回,我之前以为动态是指new int[],不用定义数组的大小,这是错的;之前尝试用指针直接传递也是错误的,int *next;只是个int型指针,而且还是没分配内存的野指针。
// 主程序
int main()
{
string x="abcababca";
string T="cababc";
cout<<KMP(x,T)<<endl;
return 0;
}
这里主要是用来测试算法正确与否的,主串和子串可以改了尝试。具体程序是这样的,我在vs2005和linux上都跑过,没问题。