【数据结构】1、串的模式匹配算法

首先我们一般求子串的位置的时候,我们可以使用这样的方法

/*
*功能:这个是定长的串的顺序存储
*时间:2015年7月15日17:16:01
*文件:SString.h
*作者:cutter_point
*/

#ifndef SSTRING_H
#define SSTRING_H

#define MAXSTRLEN 255

class SString
{
    unsigned char* ch;  //我们的串的顺序存储空间
    unsigned int length; //我们有效元素的个数
public:
    SString(){}
    SString(unsigned char c[], int lenth){ ch = new unsigned char[MAXSTRLEN + 1]; ch = c; this->length = lenth; }   //申请堆存储空间
    //一个匹配子串的算法实现
    /************************************************************************/
    /*
    返回子串T在主串S中第pos位置之后的位置,若不存在,返回0
    */
    /************************************************************************/
    int BFindex(SString T, int pos);
    //得到这个字符串的长度
    unsigned int getLength();
    unsigned char* getCh();
    //设置相应的值
    void setCh(unsigned char *c) { ch = c; }
    void setLength(unsigned int len) { length = len; }
    //unsigned char* getCh() { return ch; }
    //unsigned int getLength() { return length; }
};

#endif

实现这个类

/*
*功能:这个是定长的串的顺序存储
*时间:2015年7月15日17:16:01
*文件:SString.h
*作者:cutter_point
*/

#include "SString.h"

//得到这个字符串的长度
//int length(); 我们不知道这个数组的终止条件是不能求出来这个数组有多长的
/*
int SString::length()
{
    return 0;
}
*/

//得到这个字符串的长度
//int getLength();
unsigned int SString::getLength()
{
    return this->length;
}

//获取我们的字符串
//char* getCh();
unsigned char* SString::getCh()
{
    return this->ch;
}

//一个匹配子串的算法实现
/************************************************************************/
/*
返回子串T在主串S中第pos位置之后的位置,若不存在,返回0
*/
/************************************************************************/
//int BFindex(SString T, int pos);
int SString::BFindex(SString T, int pos)
{
    //首先我们用两个游标参数来计数我们遍历的位置
    int i = pos, j = 1;

    //然后我们用两个unsigned char 的指针来指向两个我们来比较的字符串
    unsigned char *p1 = this->getCh(), *p2 = T.getCh();
    //开始循环比较,当第一个匹配到了的时候,我们就接着比较后面的,如果中间出错,那么我们回头从新比较主串的下一个元素作为开始
    //记住我么的字符串的第一个元素时不存放东西的,我们从下标为1的那个开始存放包括1
    while (i <= this->getLength() && j <= T.getLength())
    {
        //如果匹配到了我们就一起匹配第二个元素
        if (p1[i] == p2[j])
        {
            ++i;
            ++j;
        }
        else
        {
            //否则我们就回到起点的第二个元素和子串进行比较
            i = i - j + 2;  //下一个元素
            j = 1;  //子串从头开始
        }
    }

    //最后看看是不是有完全匹配到的
    if (j > T.getLength())
        return i - T.getLength();   //获取这个位置的地方
    else
        return 0;
}

这样,我们已经可以用来进行常规的字符串的匹配了

这种匹配模式的时间复杂度为:O(n*m)

我们可以改进我们的算法,我们采用kmp算法

/*
*功能:使用kmp算法对字符串进行匹配,
*       1、一般情况下我们主串和模式串匹配到了一定程度,比如匹配到了5个(主模长度都比5大),但是第6个字符不相等
            那么我们接下来需要匹配的是在主串的什么地方开始,也可能是匹配的5个中后面有几个正好是模式串的开头,那么我们如何开始下一个子串的匹配
        2、当主串的第i个字符与模式串的第j个字符匹配不等的时候,假设此时应该和模式中第k(k<j)个字符继续比较
        3、我们已经得到匹配的字符串是k======================================从模式第k个开始比较,k-1就是开始新的匹配的时候前面不用比较的个数
        4、j=================================模式串第j个字符失配(前面那次比较得到的结果,下一次就是用到k的那次)
        5、i=================================主串第i个字符失配
        6、我们先假设p:1到p:k-1是模式串,然后s:1到s:m是主串
        7、模式串中后半部分和主串前面重合,不需要接下来匹配的部分是  { p:j-k+1 。。。 p:j-1 }  这个就是我们模式串中和主串后部分重合的地方
            j-k+1这个就是我们模式串中前面不用再和主串比较的起始位置,其实就是把失配的地方前移后面不用比较的个数j-(k-1)
        8、主串中我们也吧他前移k-1个字符,也就是我们不用比较的那些字符起始位置  { s:i-(k-1)。。。s:i-1 }
        9、由上可得模式串中已经不需要和主串比较的部分也就是  { p:1。。。p:k-1 } 和 { s:i-(k-1)。。。s:i-1 } 相等
        10、而{ s:i-(k-1)。。。s:i-1 } 就是我们后面不用比较的部分又和 { p:j-k+1 。。。 p:j-1 }相等
        11、那么  { p:j-k+1 。。。 p:j-1 }  和  { p:1。。。p:k-1 } 相等
        12、所以模式串满足11步的子串的时候,我们只需要把主串在不相等的地方开始,而子串只需要从第k个字符开始和主串的第i个字符开始比较
        13、那么我们可以根据j的值来算k的值,也就是下一个k的值是next[j],我们让next[j] = k - 1;
        14、计算k的公式就是 { p:j-k+1 。。。 p:j-1 }  ==  { p:1。。。p:k-1 } 并且 1 < next[j] < j 取最大值(也就是可以不用比较的最大值),并且{ p:j-k+1 。。。 p:j-1 }  ==  { p:1。。。p:k-1 }两个的序号不能一样
        15、-----------------------------------如何获取对应{ p:j-k+1 。。。 p:j-1 }  ==  { p:1。。。p:k-1 }的k值-------------------------------------------------------------------------------------------------------------------------
        16、我们使next[j] = k,用模式串的匹配条件和1 < next[j] < j 来取得k值的
        17、我们假设next[j] = k可以递推出下一个next[j+1]的值,通过p:k与p:j的值是否相等
        18、这个就是把模式串当做主串和模式串进行比较,寻找相同的部分
        19、我们模式串的next数据和我们要匹配的主串无关,只和我们的模式串自己相关
*时间:2015年7月18日19:12:23,2015年7月26日16:37:17
*文件:kmp.h
*作者:cutter_point
*/

#ifndef KMP_H
#define KMP_H

#include "SString.h"

class KmpString : public SString
{
    int *next;
public:
    KmpString(unsigned char *css, unsigned int len) :SString(css, len) {}
    int index_KMP(SString T, unsigned int pos);
    void get_next(SString T);
    int* getNext() { return next; }
};

#endif

实现我们的类

/*
*功能:使用kmp算法对字符串进行匹配,
*       1、一般情况下我们主串和模式串匹配到了一定程度,比如匹配到了5个(主模长度都比5大),但是第6个字符不相等
            那么我们接下来需要匹配的是在主串的什么地方开始,也可能是匹配的5个中后面有几个正好是模式串的开头,那么我们如何开始下一个子串的匹配
        2、当主串的第i个字符与模式串的第j个字符匹配不等的时候,假设此时应该和模式中第k(k<j)个字符继续比较
        3、我们已经得到匹配的字符串是k======================================从模式第k个开始比较,k-1就是开始新的匹配的时候前面不用比较的个数
        4、j=================================模式串第j个字符失配(前面那次比较得到的结果,下一次就是用到k的那次)
        5、i=================================主串第i个字符失配
        6、我们先假设p:1到p:k-1是模式串,然后s:1到s:m是主串
        7、模式串中后半部分和主串前面重合,不需要接下来匹配的部分是  { p:j-k+1 。。。 p:j-1 }  这个就是我们模式串中和主串后部分重合的地方
        j-k+1这个就是我们模式串中前面不用再和主串比较的起始位置,其实就是把失配的地方前移后面不用比较的个数j-(k-1)
        8、主串中我们也吧他前移k-1个字符,也就是我们不用比较的那些字符起始位置  { s:i-(k-1)。。。s:i-1 }
        9、由上可得模式串中已经不需要和主串比较的部分也就是  { p:1。。。p:k-1 } 和 { s:i-(k-1)。。。s:i-1 } 相等
        10、而{ s:i-(k-1)。。。s:i-1 } 就是我们后面不用比较的部分又和 { p:j-k+1 。。。 p:j-1 }相等
        11、那么  { p:j-k+1 。。。 p:j-1 }  和  { p:1。。。p:k-1 } 相等
        12、所以模式串满足11步的子串的时候,我们只需要把主串在不相等的地方开始,而子串只需要从第k个字符开始和主串的第i个字符开始比较
        13、那么我们可以根据j的值来算k的值,也就是下一个k的值是next[j],我们让next[j] = k - 1;
        14、计算k的公式就是 { p:j-k+1 。。。 p:j-1 }  ==  { p:1。。。p:k-1 } 并且 1 < next[j] < j 取最大值(也就是可以不用比较的最大值),并且{ p:j-k+1 。。。 p:j-1 }  ==  { p:1。。。p:k-1 }两个的序号不能一样
        15、-----------------------------------如何获取对应{ p:j-k+1 。。。 p:j-1 }  ==  { p:1。。。p:k-1 }的k值-------------------------------------------------------------------------------------------------------------------------
        16、我们使next[j] = k,用模式串的匹配条件和1 < next[j] < j 来取得k值的
        17、我们假设next[j] = k可以递推出下一个next[j+1]的值,通过p:k与p:j的值是否相等
        18、这个就是把模式串当做主串和模式串进行比较,寻找相同的部分
        19、我们模式串的next数据和我们要匹配的主串无关,只和我们的模式串自己相关
*时间:2015年7月18日20:42:43,2015年7月26日16:37:25
*文件:kmp.cpp
*作者:cutter_point
*/

#include "kmp.h"

#include <iostream>

//void get_next(SString T);
void KmpString::get_next(SString T) //获取next列表
{
    std::cout << T.getCh() << std::endl;
    //这来两个数j代表着是模式串中的位置,i用来计数在模式串中的遍历位置,防止访问越界
    //首先我们初始化我们的next的第一个数和我们的起始比较的位置
    /*
    unsigned char *t = T.getCh();
    int j = 2;
    this->next[j] = 0;
    int i = next[j];  //这个就是我们的上一个我们求next[j+1]的时候的next[j]
    next[2] = 1;

    while (j <= this->getLength())
    {
        if (t[j] == t[i])
        {
            ++i;
            ++j; //集体向后移一位
            //如果下一个字符相等的话,也就是p:k和p:j相等
            next[j] = i + 1;
        }
        else
        {
            i = next[i]; //这个就是我们的第p:k和p:j不相等,那么我们就求得next[k]的值,作为我们新的比较起点
        }
    }
    */
    //初始化我们的next
    this->next = new int[20]{0};
    //我们首先设定一个参数遍历主串,然后设定一个参数遍历子串
    int j = 1, i = 0;   //子串的那个还没开始比较是为0
    this->next[1] = 0;  //第一个,设定为0表示主节点是第一个的时候也就是j = 1的时候,没哟从复的地方
    unsigned char *t = T.getCh();       //得到我们的字符串
    if (T.getLength() > 1)      //首先我们传进来要进行求比较位置的参数不能为空,也就是长度大于0
    {
        //循环遍历主串
        while (j < T.getLength())
        {
            //判断子串的位置是否是0从新开始,或者我们主串的位置和子串的位置比较
            if (i == 0 || t[j] == t[i])
            {
                //如果是第一个或者匹配相等
                ++j;    //主串
                ++i;    //子串
                this->next[j] = i;  //我们把主串的和子串匹配的位置记住
            }
            else
            {
                //如果这个p:j和p:k不相等,也就是t[j] != t[i]不相等的时候
                i = next[i];
            }
        }
    }

}

/*
利用模式T的next的值,求T在S主串中第pos个字符之后的位置
*/
int KmpString::index_KMP(SString T, unsigned int pos)
{
    int i = pos, j = 1;  //第一个是我们主串开始的位置,后面是我们模式串开始的位置
    unsigned char *s = this->getCh();
    unsigned char *t = T.getCh();
    while (i <= this->getLength() && j <= T.getLength())
    {
        //在长度范围内
        if (j == 0 || s[i] == t[j])
        {
            ++i;
            ++j;
        }
        else
        {
            j = next[j];
        }
    }
    //循环结束之后我们判断一下是遍历到了最后的没有找到,还是找到了
    if (j > T.getLength()) //说明匹配到了最后的位置
        return i - T.getLength();
    else
        return 0;
}

我们的主函数

/*
*功能:串的所有执行策测试
*时间:2015年7月15日17:16:01,2015年7月21日20:33:10,2015年7月26日16:37:31
*文件:SString.h
*作者:cutter_point
*/

#include <iostream>

#include "kmp.h"
#include "SString.h"

using namespace std;

int main()
{
    unsigned char c[] = " acabaabaabcacaabc";
    unsigned char c2[] = " abaabcac";
    SString T;
    T.setCh(c2);
    T.setLength(8);
    KmpString *ks = new KmpString(c, 16);
    ks->get_next(T);

    int *p = ks->getNext();

    for (int i = 0; i < 21; ++i)
    {
        cout << *(p + i) << "\t";
    }
    cout << endl;
    cout << ks->index_KMP(T, 1) << " hahaah " << endl;

    return 0;
}

/*
int main()
{
    unsigned char ch[] = {‘ ‘, ‘a‘,‘b‘,‘a‘,‘b‘,‘c‘,‘a‘,‘b‘,‘c‘,‘a‘,‘c‘,‘b‘,‘a‘,‘b‘};
    unsigned char ch2[] = {‘ ‘, ‘a‘,‘b‘,‘c‘};
    int lengch = 14;
    int lengch2 = 3;
    SString *s = new SString(ch, lengch);
    SString t;
    t.setCh(ch2);
    t.setLength(lengch2);
    cout<<s->BFindex(t, 1);

    return 0;
}
*/

输出结果:

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2025-01-19 22:33:16

【数据结构】1、串的模式匹配算法的相关文章

24、蛤蟆的数据结构笔记之二十四串的模式匹配算法

24.蛤蟆的数据结构笔记之二十四串的模式匹配算法 本篇名言:"燧石受到的敲打越厉害,发出的光就越灿烂. -- 马克思" 来看下两个算法,BF和KMP算法在串的模式匹配中实现. 欢迎转载,转载请标明出处: 1.  BF算法 BF(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符:若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果.B

《数据结构》之串的模式匹配算法——KMP算法

1 //串的模式匹配算法 2 //KMP算法,时间复杂度为O(n+m) 3 #include <iostream> 4 #include <string> 5 #include <cstring> 6 using namespace std; 7 8 //-----串的定长顺序存储结构----- 9 #define MAXLEN 255 //串的最大长度 10 typedef struct { 11 char ch[MAXLEN + 1]; //存储串的一维数组 12

第四章:2.串 -- 串的模式匹配算法(KMP)

前言: 目录: 1.串类型的定义 2.串的表示和实现 3.串的模式匹配算法 4.串操作应用举例 正文: 串的模式匹配即,在给定主串S 中,搜索子串T 的位置,如果存在T 则返回其所在位置,否则返回 0 串的模式匹配算法 主串 S: a b c a b c d s v t 子串 T: a b c d 一.原始算法 匹配一旦失败,子串即向右移动一个单位,直到完全匹配停止. 第一次匹配:(注:红色代表不匹配(失配)) S: a b c a b c a b c d s v t   T: a b c d

串的模式匹配算法(KMP)

算法: #include<IOSTREAM> using namespace std; #define MAXSIZE 100 void calNext(const char *T,int *next);//T为模式串,next为预判数组 int kmp_match(const char *S,const char *T);//在主串S中寻找模式串T,如果找到返回其位置,否则返回-1.位置从0开始 void calNext(const char *T,int *next) { int n =

串的模式匹配算法(BF算法和KMP算法)

串的模式匹配算法 子串的定位操作通常称为串的 模式匹配,其中T称为 模式串. 一般的求子串位置的定位函数(Brute Force) 我写java的代码是这样的 int index(String S,String T,int pos){ char[] s_arr = S.toCharArray(); char[] t_arr = T.toCharArray(); int i,j,k;//i是主串S的指针,j是模式串的指针 if(pos < 0 || pos>S.length() || S.len

串、串的模式匹配算法(子串查找)BF算法、KMP算法

串的定长顺序存储#define MAXSTRLEN 255,//超出这个长度则超出部分被舍去,称为截断 串的模式匹配: 串的定义:0个或多个字符组成的有限序列S = 'a1a2a3--.an ' n = 0时为空串串的顺序存储结构:字符数组,串的长度就是数组末尾'\0'前面的字符个数数组需在定义时确定长度,有局限性数组的最大长度二:串的堆分配存储表示typedef struct { char *ch; //若是非空串,则按串长分配存储区 //否则ch为空 int length; //串长度}HS

数据结构- 串的模式匹配算法:BF和 KMP算法

Brute-Force算法的思想 1.BF(Brute-Force)算法 Brute-Force算法的基本思想是: 1) 从目标串s 的第一个字符起和模式串t的第一个字符进行比较,若相等,则继续逐个比较后续字符,否则从串s 的第二个字符起再重新和串t进行比较. 2) 依此类推,直至串t 中的每个字符依次和串s的一个连续的字符序列相等,则称模式匹配成功,此时串t的第一个字符在串s 中的位置就是t 在s中的位置,否则模式匹配不成功. Brute-Force算法的实现 c语言实现: [cpp] vie

4.2 串的模式匹配算法

<?php header("content-type:text/html;charset=utf-8"); class Linear_string{ /** * 串模式匹配算法 * *包括 * 1.串的初始化 __contruct() * 2.朴素的模式匹配算法 index() * 3.KMP模式匹配算法 * 4.改进的KMP模式匹配算法 */ private $string; private $length; //构造函数 public function __construct

串-KMP模式匹配算法(nextval数组)

#include <stdio.h> #include <stdlib.h> #include <string.h> void get_next(char T[100],int *next); int Index_KMP(char S[100],char T[100],int pos); int main() { int n; char S[100],T[100]; gets(S); gets(T); n=Index_KMP(S,T,2); printf("%