字符串总结(KMP)

来源:互联网 发布:淘宝海景房店铺氛围图 编辑:程序博客网 时间:2024/05/21 06:26

字符串上的操作
*今天来总结一下关于串的问题,串包括字符串和数组
*这里一字符串为例:现在来有关字符串的一些算法
*1、逆转字符串revstr(s)
*2、求字符串中的最长回文子串lhw(s)
*3、求字符串的最长前缀的最长后缀lpre_Lpos(s)
*4、求字符串的最长前缀的最长后缀的优美的方法和得到next的数组getnext(s,next)
*5、朴素的字符串的模式匹配算法BF(T,P)
*6、字符串的模式匹配算法KMP(T,P,next)

/** *1、逆转字符串revstr(s) *这个函数的实现在C语言和C++中都有实现 *C:strrev(),可以使用,不是C语言的标准函数 *C++中是利用泛型算法的迭代器实现 */char * revstr(char *s){    int len=strlen(s);    int i,j;    for(i=0,j-len-1;i<=j;i++,j--){        char t=s[i];        s[i]=s[j];        s[j]=t;    }    return s;}/** *求字符串中的最长回文子串lhw(s) *首先判断字符串是不是回文串定义函数 *int is_huiwen(*s,i,j),是回文返回1 */int is_huiwen(char *s,int i,int j){    while(i<=j){        if(s[i]!=s[j]) return 0;        else {            ++i; --j;        }    }    return 1;//是回文}int lhw(char *s){    int m=1;//最短是1    int len=strlen(s);    for(int i=0;i<len-1;i++){        for(int j=i+1;j<len;j--){            if(is_huiwen(s,i,j)&&m<(j-i+1)){                m=j-i+1;            }        }    }    return m;}/** *求字符串的最长前缀的最长后缀lpre_lpos(s) *首先说一下什么是前缀和后缀, *X=WY, WY为链接字符串W和Y,此时W为X的前缀 *X=YW, 同理可知,此时W为X的后缀 *例如:X="abcdeabc","abc"既是X的前缀又是X的后缀 *并且"abc"是X的最长前缀的最长后缀。 *前缀:以第一个字符开始的所有子串都是X的前缀 *后缀:以最后一个字符结尾的所有子串都是X的后缀 *关于这个问题,我们先给出一个笨拙的算法, *我们知道对于字符串s的最长前缀的最长后缀的长度0<=X<=n-1 */int lpre_lpos(char *s){    int len=strlen(s);    for(int k=len-1;k>=0;k--){        int j=len-k;        int i=0;        while(s[i]==s[j]){            i++; j++;        }        if(j==len){//此时已经到最后一个字符,因为是从大到小模拟,一定是最大值            return k;        }    }    return 0;//最小值}/** *朴素的字符串的模式匹配算法BF(T,P) */int BF(char *T,char *P){    //开使从下标1开始,输入处理:scanf("%s",s+1);    int n=strlen(T+1);//此时得到的是真实长度,即能取到下标m    int m=strlen(P+1);    int s,k;    for(s=0;s<=n-m;s++){//遍历所有可能的起点        for(k=1;k<=m;k++){            if(T[s+k]!=P[k]) break;        }        if(k==m+1){            //return s;//此时返回的第一次匹配成功的下标            printf("%d\n",s);//输出所有可以匹配的下标        }    }    return -1;//匹配不成功,返回-1}Next[]+KMP/** *Next[]数组求最大前缀的最大后缀的长度 *主要用于KMP中 *这里给出的代码是从下标1开始的,我们处理的时候 *需要输出的时候处理一下就行了,还有就是求长度的时候处理 *scanf("%s",s+1); */int getNext(char *p,int *next){    //从下标1开始计算的,注意下标之间的关系,这个函数主要用于KMP    int len=strlen(p+1);    next[1]=0;    int k=0;    for(int q=2;q<=len;q++){        while(k>0&&p[k+1]!=p[q]){            k=next[k];        }        if(p[k+1]==p[q]){            k=k+1;        }        next[q]=k;    }    return next[len];}/** *KMP,避免指针回溯 */int KMP(char *T,char *P,int *next){//next为getNext函数得到的值,    //开使从下标1开始,输入的需要:scanf("%s",s+1);    int n=strlen(T+1);//此时得到的是真实长度,即能取到下标m    int m=strlen(P+1);    int i,q=0;    for(i=1;i<=n;i++){        while(q>0&&P[q+1]!=T[i]){            q=next[q];//一直向前找到可以匹配的字符,                      //这里听不好理解的,我会给出好的理解的方法的        }        if(P[q+1]==T[i]){//相等匹配下一个模式            q=q+1;        }        if(q==m){//这里不要比较m+1,因为比较的时候是q+1            //return i-m;//返回第一个匹配的位置            printf("%d\n",i-m);//输出所有匹配的下标            q=next[q];//进行下次匹配        }    }    return -1;//没有匹配的位置}

下面来讲解一下KMP,KMP的优势在于避免了文本串的指针回溯,首先来看一下《算法导论》的解释,由于不会再word里面做图片,这里就只能拍照了。个人认为KMP算法并不很难,我也是看了很多遍的,最后我认为最难的是,求next的数组,就最大前缀的最大后缀,那么为什么求出这个,就能再KMP中使用呢,我认为如果真是理解不了即不要去想太多,就是照着代码进行理解,最难理解的就是这段代码,while(k>0&&p[k+1]!=p[q]){
k=next[k];
}
为甚么不相等的时候要一直往前中呢,我们首先的注意next的求值过程,我们是从前往后求的,那么k的下标对应的字符是跟谁比较进行得出的呢,很明显是与next[k]比较的,那么现在加入P[k]==T[i],那么一定会有P[next[k]]=T[i],如果P[k+1]!=T[i+1],我们当然可以比较P[next[k]+1]==T[i+1]??,这是在KMP中比较是的代码,那么在求next的数组时,为什么也是这样呢,我们知道在匹配过程中,如果T[i]==P[q],我们当然可以直接用P[q]替换T[i],就此可以自己去想KMP。
解释的不太清楚,请见谅啊。

这里写图片描述

1 0
原创粉丝点击