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