字符串总结(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。

解释的不太清楚,请见谅啊。

时间: 2024-10-26 11:39:16

字符串总结(KMP)的相关文章

字符串的KMP算法替换

1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 6 7 class myString 8 { 9 private: 10 string mainstr; 11 int size; 12 void GetNext(string p,int next[]); 13 int KMPFind(string p,int next[]); 14 public: 15 myString(); 16 //~myS

字符串匹配算法KMP算法

数据结构中讲到关于字符串匹配算法时,提到朴素匹配算法,和KMP匹配算法. 朴素匹配算法就是简单的一个一个匹配字符,如果遇到不匹配字符那么就在源字符串中迭代下一个位置一个一个的匹配,这样计算起来会有很多多余的不符合的匹配做了冗余的比较.假设源字符串长n,字串长m 该算法最差时间复杂度为 m*(n-m+1),记为O(n*m);这里不做过多解释朴素匹配算法. KMP算法: kmp算法不是在源字符串中下手,他是从字串下手,比如我要在源字符串(acabaabaabcacaabc)中匹配一个字符串字串(ab

关于两个字符串的kmp比对算法

关于两个字符串的kmp比对算法 假设有字符串X和Y,满足len(X)>len(Y),要比对这两个字符串. 我们知道,最朴实的方法,就是现将二者对齐,然后依次比对对应位置的字符.如果能匹配到Y最后位置,则匹配成功:如果匹配失败,则将Y右移一位,再从头进行匹配. 设字符串X为dababeabafdababcg:字符串Y为ababc. 这种比对方法如下所示: 起始时,二者对其,第一个字符不匹配 :| :dababeabafdababcg :ababc 右移一位,比对位置移动到Y起始位置 : | :da

字符串模式匹配KMP算法中的next数组算法及C++实现

一.问题描述: 对于两个字符串S.T,找到T在S中第一次出现的起始位置,若T未在S中出现,则返回-1. 二.输入描述: 两个字符串S.T. 三.输出描述: 字符串T在S中第一次出现的起始位置,若未出现,则返回-1. 四.输入例子: ababaababcbababc 五.输出例子: 5 六.KMP算法解析: KMP算法分为两步,第一步是计算next数组,第二步是根据next数组通过较节省的方式回溯来比较两个字符串. 网络上不同文章关于next数组的角标含义略有差别,这里取参考文献中王红梅<数据结构

字符串匹配算法-KMP

举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"? 在上面这个例子中,字符串"BBC ABCDAB ABCDABCDABDE"称为主串,字符串"ABCDABD"称为模式串 许多算法可以完成这个任务,Knuth-Morris-Pratt算法(简称KMP)是最常用的之一.下面,我用自己的语言,解释KMP算法. 1.首先,主串"BBC ABCDA

字符串匹配算法——KMP算法

1.字符串匹配 字符串匹配是计算机的基本任务之一. 字符串匹配是什么?举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"? 许多算法可以完成这个任务,Knuth-Morris-Pratt算法(简称KMP)是最常用的之一.它以三个发明者命名,起头的那个K就是著名科学家Donald Knuth(<计算机程序设计艺术>的作者). 2.KMP算法 这个算法不太容易理解,网上有很多解释,但

字符串匹配算法KMP详细解释——深入理解

1. 前言 字符串匹配是一个经典算法问题,展开来讲各类问题多达几十种,有名称的算法也不下三十种,所以需要深入学习的东西有很多.这次我们来探讨一个最简单的问题,假设现在随机输入一个长度为m的主串T,另外输入一个长度为n(n≤m)的字符串P,我们来判断字符串P是否是主串T的一个子串(即能否从T中随机取出与P同长的一段字符串,与P完全匹配). 2. 蛮力匹配法 问题很简单,当然也有最直接.最直观也是最好想到的方法,蛮力串匹配.即两个字符串像物流传送带一般,主串固定,子串一步步像前移动,一位位匹配比较,

[小明学算法]6.字符串匹配算法---KMP

1.简介  字符串匹配就是看看那字符串b是不是字符串a的子串.常用的Knuth-Morris-Pratt 算法,又称KMP算法. 2.主要思想 当patter在某一位置与string匹配失败时,我们除了知道从string的这个位置进行匹配失败这个结果外,是否可以从前面的匹配中获得更多的信息呢.即当前匹配点匹配失败之后,向右滑动的距离是可以提前计算出来的. 3.举例 abcabcabcdef   --------- string abcabcdef         --------- patter

查找字符串的 KMP 算法

查找字符串是我们平常编程过程中经常遇到的,现在介绍一种查找字符串算法,增加程序的执行速度. 通常我们是这么写的: /* content: search a string in a othor string author: lw date: 2015-01-30 target: kmp algorithm */ #include <stdio.h> #include <string.h> void compare(char * sourcestr, char * targetstr)

字符串查找KMP算法(转)

如果你用过ctrl+F这个快捷键,那么你有很大的概率使用过这个算法,这就是在待查找字符串(可能有成千上万个字符)中找出模式串(比较小,可能有几个字符),可能找到大于或者等于1次的位置.例如,在ababcd中找出abc.这里介绍算法思想,只给出了第一次出现的位置. 一.算法思想 传统算法是从匹配串第一字符开始和模式串比较,直到遇到不符合的字符,然后从匹配串的下一个字符开始,重复上面的过程.代码如下: void find(char t[],char p[]){ int m = strlen(t);