数据结构——串(kmp算法)

是由零个或多个字符组成的有限序列,一般记为S=‘a1a2……an’
若两个串长度相等且每个对应位置的字符都相等时,称这两个串是相等的

子串

S=‘Hello World’ S1=‘Hello’ S2’World’ S1和S2都是S的子串

串的储存结构

1.定长顺序存储和堆存储结构

#define MAXLEN 255
//定长
typedef struct {
     
    char ch[MAXLEN];
    int length;
}SString;
//堆存储
typedef struct {
     
    char *ch;
    int length;
}HString;

2.链式存储结构
每个单链表表示一个字母

串的基本操作

StrAssign(&T,chars):赋值操作。把串t赋值为chars
StrCompare(S,T):比较操作,若S>T则返回值>0;若S=T,则返回0;若S StrLength(S):求串长。
SubString(&Sub,S,pos,len)求子串.用Sub储存S中从POS开始长度为len的子串
concat(&T,S1,S2):串联接用T返回连接S1和S2的新串
最小操作子集

串的模式匹配

利用基本操作进行匹配

Index(S,T,pos):定位操作,若主串S中存在与串T值相同的子串,则返回它在主串中的第POS个字符后第一次出现的位置,否则返回0

int Index(SString S,SString T,int pos){
     
    if(pos>0){
     
        int n=StrLength(S);
        int m=StrLength(T);
        int i=pos;
        SString sub;
        while(i<n-m+1){
     
            SubString(sub,S,i,m);
            if (StrCompare(sub,T)!=0)
                i++;
            else
                return i;
        }
    }
    return 0;
}

简单的模式匹配算法

原理:

设置变量i表示主串的位置,i=1表示主串的第一个元素,设置变量j表示匹配子串的位置,j=1表示第一个元素,比较主串和配置子串的第一个元素,如果相等则,一次比较后面的元素,直到j大于匹配子串的长度则匹配成功,如果主串i对应元素与和匹配子串j对应元素不相等,则i返回j-2个位置,j重置为1,重新开始匹配,若主串一直没有匹配成功而剩余匹配元素低于匹配子串长度,则直接退出数据结构——串(kmp算法)_第1张图片

简单匹配代码

//简单匹配算法
int Index2(SString S,SString T,int pos){
     
    int i=pos,j=1;
    while (i<=S.length-T.length&&j<=T.length){
     
        if (S.ch[i]==T.ch[j]){
     
            i++;
            j++;
        } else{
     
            i=i-(j-2);
            j=1;
        }
    }
    if (j>T.length)
        return i-T.length;
    else
        return 0;
}

KMP算法

原理

字符串的前缀,后缀匹配部分

1.前缀:一个字符串,从前面开始排,所包含的所有子串。如:“abcdefg”的前缀就是a,ab,abc,abcd,abcde,abcdef
2.后缀:一个字符串,从后面开始排,所包含的所有子串。如:“abcdefg”的后缀就是g,fg,efg,defg,cdefg,bcdefg
3.匹配值为;一个字符串的前缀和后缀共同的元素的最大长度,
如,上面的例子中没有一个元素是相等的则部分匹配值为0
如:“aba”的前缀是a,ab,后缀是a,ba ,其中有共同的元素a,而a的长度是1,所以“aba”的匹配值为1
如:“ababa”的前缀是a,ab,aba,abab,后缀a,ba,aba,baba,其中有共同的元素a和aba而其中aba的长度是3最大,所以ababa的匹配值是3
部分匹配值(MP):字符串和他所有的前缀的匹配值,如:ababa,前缀a的匹配值为0,前缀ab的匹配值为0,前缀aba的匹配值为1,前缀abab的匹配值为2,字符串本身ababa的匹配值为3,则字符串的部分匹配值(MP)为00123

计算部分匹配值
//kmp计算部分匹配值
void get_next(SString T,int next[]){
     
    int i=1,j=0;
    next[1]=0;
    while (i<T.length){
     
        if (j==0||T.ch[i]==T.ch[j]){
     
            ++i;++j;next[i]=j;
        } else
            j=next[j];
    }
}

字符串匹配

基本原理和简单匹配算法一样,都是主串和匹配串分别设置指针,一个一个比较,最后找出子串,但是简单匹配算法有个缺点,每次比较失败之后i会退回j-2个位置,但是中间明显有一部分是匹配不成功的(因为不包含匹配算法的任何前缀),于是为了避免i回退,我们通过前缀和后缀的比较,如果比较的元素可能出现匹配值的前缀我们再去比较,如果根本不包含匹配值的前缀那就直接跳过刚刚比较过的这一部分,而如果存在相同的部分,只需要把j往后退对应的部分匹配值,这样的话i就永远不会回退。

如图:如果匹配过程中发现不匹配的元素,按照简单匹配算法则要再多匹配3次才可以匹配成功,而通过KMP算法则可以跳过中间两个浅绿色的部分,如图原来已经匹配了5个元素(黄色方框的元素),只需要经过前缀后缀的比较发现,前缀中有个ab,后缀中也有个ab,中间没有相同的前后缀,则直接跳过中间的a和c直接从第二个b开始比较,而因为前面已经比较过了bacba所以后面两个ba已经比较过了,所以字符串的j直接从第三个c开始比较就可以了,不用退回到1开始比较(退回的个数为对应部分匹配值得个数:2个)
数据结构——串(kmp算法)_第2张图片

int Index_KMP(SString S,SString T,int next[],int pos){
     
    int i=pos ,j=1;
    while (i<=S.length&&j<=T.length) {
     
        if (j == 0 || S.ch[i] == T.ch[j]) {
     
            i++;
            j++;
        } else {
     
            j = j - 1 - next[j - 1];
        }
    }
        if (j>T.length){
     
            return i-T.length;
        } else {
     
            return 0;
        }
}

KMP改进算法

你可能感兴趣的:(数据结构)