码迷,mamicode.com
首页 > 其他好文 > 详细

字符串总结(KMP)

时间:2015-03-28 00:00:05      阅读:380      评论:0      收藏:0      [点我收藏+]

标签:模式匹配   字符串   后缀   kmp   

字符串上的操作
*今天来总结一下关于串的问题,串包括字符串和数组
*这里一字符串为例:现在来有关字符串的一些算法
*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。
解释的不太清楚,请见谅啊。

技术分享

字符串总结(KMP)

标签:模式匹配   字符串   后缀   kmp   

原文地址:http://blog.csdn.net/fool_ran/article/details/44683595

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!