数据结构(三)串---KMP模式匹配算法实现及优化

KMP算法实现

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXSIZE 40

typedef int ElemType;
typedef int Status;

//设置串的存储结构
typedef char String[MAXSIZE+1];

//生成串相关
Status StrAssign(String S,char *chars);    //生成一个其值等于字符串常量的chars的串T
Status StrCopy(String T, String S);        //串S存在,由串S复制得到串T
Status Concat(String T, String S1, String S2);                //用T返回由S1和S2连接而成的新串

//基础操作相关
Status ClearString(String S);     //串S存在,将串清空
Status StringEmpty(String S);     //若串为空,返回true,否则false
int StringLength(String S);     //返回串S的元素个数,长度

//比较串,索引串相关
int StrCompare(String S, String T);            //若S > T返回 > 0, = 返回0, < 返回 < 0
Status SubString(String Sub, String S,int pos,int len); //串S存在,返回S由pos起,长度为len的子串到Sub
int Index(String S, String T,int pos);                //主串S,子串T,返回T在S中位置

//增删改相关
Status Replace(String S, String T, String V);        //串S,T和V存在,T非空,用V替换主串S中T串
Status StrInsert(String S, int pos, String T);    //在主串S中的pos位置插入串T
Status StrDelete(String S,int pos,int len);    //串S存在,从串S中删除第pos个字符串起长度为len的子串

void PrintStr(String S);

//生成串相关
//生成一个其值等于字符串常量的chars的串T
Status StrAssign(String S, char *chars)
{
    int i;
    if (strlen(chars) > MAXSIZE)
        return ERROR;
    else
    {
        S[0] = strlen(chars);
        for (i = 1; i <= S[0];i++)
            S[i] = *(chars+i-1);
        return OK;
    }
}

//串S存在,由串S复制得到串T
Status StrCopy(String T, String S)
{
    int i;
    for (i = 0; i <= S[0];i++)
        T[i] = S[i];
    return OK;
}

//用T返回由S1和S2连接而成的新串,若是超出,会截断,但是会进行连接,返回FALSE
Status Concat(String T, String  S1, String S2)
{
    int i,j,interLen;    //interLen是截断后S2剩余长度

    if (S1[0] + S2[0] > MAXSIZE)
        interLen = MAXSIZE - S1[0];
    else
        interLen = S2[0];

    T[0] = S1[0] + S2[0];
    for (i = 1; i <= S1[0]; i++)
        T[i] = S1[i];
    for (j = 1; j <= interLen; j++)
        T[i+j-1] = S2[j];
    if (interLen != S2[0])
        return ERROR;
    return OK;
}

//基础操作相关
//串S存在,将串清空
Status ClearString(String S)
{
    S[0] = 0;
    return OK;
}

//若串为空,返回true,否则false
Status StringEmpty(String S)
{
    if (S[0] != 0)
        return FALSE;
    return TRUE;
}

//返回串S的元素个数,长度
int StringLength(String S)
{
    return S[0];
}

//比较串,索引串相关
//若S > T返回 > 0, = 返回0, < 返回 < 0
int StrCompare(String S, String T)
{
    int i;
    for (i = 1; i <= S[0] && i <= T[0]; i++)
        if (S[i] != T[i])
            return S[i] - T[i];
    return S[0]-T[0];    //若是相同比较长度即可
}

//串S存在,返回S由pos起,长度为len的子串到Sub
Status SubString(String Sub, String S, int pos,int len)
{
    int i;
    if (pos<1 || len<0 || pos + len - 1 > S[0] || pos>S[0])
        return ERROR;
    for (i = 0; i < len;i++)
        Sub[i + 1] = S[pos + i];
    Sub[0] = len;
    return OK;
}

//主串S,子串T,返回T在S中位置,pos代表从pos开始匹配
//或者一次截取一段进行比较为0则找到
int Index(String S, String T, int pos)
{
    int i, j;
    i = pos;
    j = 1;
    while (i<=S[0]-T[0]+1&&j<=T[0])
    {
        if (S[i]==T[j])
        {
            j++;
            i++;
        }
        else
        {
            i = i - j + 2;    //注意这个索引的加2
            j = 1;
        }
    }
    if (j > T[0])
        return i - T[0];
    return 0;
}

//增删改相关
//串S,T和V存在,T非空,用V替换主串S中T串
Status Replace(String S, String T, String V)
{
    int idx=1;
    if (StringEmpty(T))
        return ERROR;

    while (idx)
    {
        idx = Index(S, T, idx);
        if (idx)
        {
            StrDelete(S, idx, StringLength(T));
            StrInsert(S, idx, V);
            idx += StringLength(V);
        }
    }
    return OK;
}

//在主串S中的pos位置插入串T,注意:若是串满,则只插入部分
Status StrInsert(String S, int pos, String T)
{
    int i,interLength;
    if (S[0] + T[0] > MAXSIZE)    //长度溢出
        interLength = MAXSIZE - S[0];
    else
        interLength = T[0];

    for (i = S[0]; i >= pos;i--)
        S[interLength + i] = S[i];    //将后面的数据后向后移动

    //开始插入数据
    for (i = 1; i <= interLength; i++)
        S[pos + i - 1] = T[i];

    S[0] += interLength;

    if (interLength != T[0])
        return ERROR;
    return OK;
}

//串S存在,从串S中删除第pos个字符串起长度为len的子串
Status StrDelete(String S, int pos,int len)
{
    int i;
    if (pos < 1 || len<1 || pos + len - 1>S[0])
        return ERROR;

    //将数据前移
    for (i = pos+len; i <= S[0];i++)
        S[i-len] = S[i];

    S[0] -= len;
    return OK;
}

void PrintStr(String S)
{
    int i;
    for (i = 1; i <= StringLength(S);i++)
    {
        printf("%c", S[i]);
    }
    printf("\n");
}

串的顺序存储结构

//通过计算返回子串T的next数组
void get_next(String T, int* next)
{
    int m, j;
    j = 1;    //j是后缀的末尾下标
    m = 0;    //m代表的是前缀结束时的下标
    next[1] = 0;
    while (j < T[0])
    {
        if (m == 0 || T[m] == T[j])    //T[m]表示前缀的最末尾字符,T[j]是后缀的最末尾字符
        {
            ++m;
            ++j;
            next[j] = m;
        }
        else
            m = next[m];    //若是字符不相同,则m回溯
    }
}
int Index_KMP(String S, String T, int pos)
{
    int i = pos;
    int j = 1;
    int next[MAXSIZE];
    get_next(T, next);
    while (i<=S[0]&&j<=T[0])  //其实现与BF算法相似,不过不同的是i不进行回溯,而是将j进行了修改
    {
        if (j==0||S[i]==T[j])
        {
            ++i;
            ++j;  //若完全匹配后,j就会比模式串T的长度大一
        }
        else  //不匹配时,就使用next数组获取下一次匹配位置
        {
            j = next[j];
        }
    }
    if (j > T[0])
        return i - T[0];
    else
        return 0;
}

int main()
{
    int i, j;
    String s1,s2,t;

    char *str = (char*)malloc(sizeof(char) * 40);
    memset(str, 0, 40);
    printf("enter s1:");
    scanf("%s", str);
    if (!StrAssign(s1, str))
        printf("1.string length is gt %d\n", MAXSIZE);
    else
        printf("1.string StrAssign success\n");

    printf("enter s2 to match:");
    scanf("%s", str);
    if (!StrAssign(s2, str))
        printf("1.string length is gt %d\n", MAXSIZE);
    else
        printf("1.string StrAssign success\n");

    i = Index_KMP(s1, s2, 1);
    printf("index:%d", i);

    system("pause");
    return 0;
}

main函数

KMP算法优化---对next数组获取进行优化

原来我们获取的next数组是由缺陷的

我们可以发现j5与i5失配,所以按照上面的next值,会去匹配j4-j3-j2-j1,但是我们从前面的思路启发知道,当我们知道j1=j2=j3=j4=j5,而j5≠i5,那么我们完全可以知道j1到j4也是不与i5匹配,所以,我们这里做了太多的重复匹配。这就是我们需要优化的地方
由于T串的第二三四五位的字符都与首位的‘a‘相等,那么可以用首位next[1]的值去取代与他相等的字符后续的next[j]值 

改进方法:

我们将新获取的next值命名为nextval,则新的nextval与他同列的next值有关,我们找的一next[j]为列值的新的j列,将字符进行比较,若是相同,则将该列的next值变为现在的nextval

例如:

推导1:

 第一步:当j=1时,nextval=0

第二步:获取j=2时,nextval[2]的值,首先我们需要获取next[2]值为1,然后我们将这个值作为新得前缀获取j=1时的T串数据‘a‘,发现他与当前j=2处的字符‘b‘不同,那么nextval[2]不变,与原来的next[2]值一样,为1

第三步:获取j=3时,nextval[3]的值,我们先获取next[3]的值为1,然后将这个值作为新的索引获取j=该值处的字符T[1]=‘a‘,发现T[3]=T[1],所以当前的nextval值为j=1处的nextval值,即nextval[3]=nextval[1]=0

第四步:获取j=4时,nextval[4]的值,相应的next[4]为2,获取T[2]字符‘b’,与当前所以T[4]=‘b‘相同,那么当前nextval[4]=nextval[2]=1

第五步:获取j=5时,nextval[5]的值,相应next[5]=3,查看T[3]字符为‘a‘,而当前T[5]=‘a‘,相同,那么nextval[5]=nextval[3]=0

第六步:获取j=6时,nextval[6]的值,相应next[6]=4,查看T[4]字符为‘b‘,而当前T[6]=‘a‘,不相同,那么nextval[5]就等于原来的next[5]值为4

第七步:获取j=7时,nextval[7]的值,相应next[7]=2,查看T[2]字符为‘b‘,而当前T[7]=‘a‘,不相同,那么nextval[7]就等于原来的next[7]值为2

第八步:获取j=8时,nextval[8]的值,相应next[8]=2,查看T[2]字符为‘b‘,而当前T[8]=‘b‘,相同,那么nextval[7]=nextval[2]=1

第九步:获取j=9时,nextval[9]的值,相应next[9]=3,查看T[3]字符为‘a‘,而当前T[9]=‘a‘,相同,那么nextval[9]=nextval[3]=0

推导2:对上面进行优化的步骤演示

第一步:当j=1时,nextval=0

第二步:当j=2时,next[2]=1,T[2]=T[1]=‘a‘,nextval[2]=nextval[1]=0

第三步:当j=3时,next[3]=2,T[3]=T[2]=‘a‘,nextval[3]=nextval[2]=0

第四步:当j=4时,next[4]=3,T[4]=T[3]=‘a‘,nextval[4]=nextval[3]=0

第五步:当j=5时,next[5]=4,T[5]=T[4]=‘a‘,nextval[5]=nextval[4]=0

第六步:当j=6时,next[6]=5,T[6]=‘x‘,T[5]=‘a‘,T[6]≠T[5],nextval[6]=next[6]=5

nextval数组优化代码

void get_nextval(String T, int* nextval)
{
    int m, j;
    j = 1;    //j是后缀的末尾下标
    m = 0;    //m代表的是前缀结束时的下标
    nextval[1] = 0;
    while (j < T[0])
    {
        if (m == 0 || T[m] == T[j])    //T[m]表示前缀的最末尾字符,T[j]是后缀的最末尾字符
        {
            ++m;
            ++j;
            if (T[j] != T[m])        //若当前字符与前缀字符不同
                nextval[j] = m;        //则当前的j为nextval在i位置的值
            else
                nextval[j] = nextval[m];    //若与前缀字符相同,则将前缀字符的nextval值赋给nextval在i位置的值
        }
        else
            m = nextval[m];    //若是字符不相同,则m回溯
    }
}

原文地址:https://www.cnblogs.com/ssyfj/p/9457898.html

时间: 2024-11-07 16:13:51

数据结构(三)串---KMP模式匹配算法实现及优化的相关文章

串-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("%

数据结构(三)串---KMP模式匹配算法之获取next数组

(一)获取模式串T的next数组值 1.回顾 我们所知道的KMP算法next数组的作用 next[j]表示当前模式串T的j下标对目标串S的i值失配时,我们应该使用模式串的下标为next[j]接着去和目标串失配的i值进行匹配 而KMP算法的next求值函数 我们可以知道next除了j=1时,next[1]为0,其他情况都是比较前缀和后缀串的相似度(第三种情况是当相似度为0时,next值为0+1=1) next数组,是用来评判前后缀的相识度,而next值,则是等于相似度加一 2.思考 虽然我们知道是

Java数据结构-串及其应用-KMP模式匹配算法

串(string)是由零个或多个宇符组成的有限序列,又名叫字符串. 定义的解释: ??串中的字符数目n称为串的长度,定义中谈到"有限"是指长度n是一个有限的数值. ??零个字符的串称为空串(null string),它的长度为零,可以直接用两双引号一表示,也可以用希腊Φ字母来表示. ??所谓的序列,说明串的相邻字符之间具有前驱和后继的关系. 下面是串的一些概念性东西: ??空格串,是只包含空格的串.注意它与空串的区别,空格串是有内容有长度的,而且可以不止一个空格. ??子串与主串,串中

《数据结构》之串的模式匹配算法——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

数据结构--KMP模式匹配算法

今天,在看数据结构--串这一章节时,看到了KMP算法,相对较复杂些,在此单独做下整理. kmp算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特--莫里斯--普拉特操作(简称KMP算法).KMP算法的关键是根据给定的模式串W1,m,定义一个next函数.next函数包含了模式串本身局部匹配的信息. 例子: 假如我们要比较两个字符串是否相等. 在T串中查找S串.我们用最笨的方法去想,就是将T串与S串中的每一个元素一一去匹配,

第四章: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

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

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

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

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

串的模式匹配算法(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 =