数据结构与算法整理3——BP算法和KMP算法(C语言)
目录
1、字符串的基本操作
1)串的定义:字符串(也就是串)是0个或多个字符序列组成的有限序列。
- 串就是数据元素为单个字符的特殊线性表。
- “qhjkdcbjsb”(隐含结束符\0)就是一个字符串,引号起界定作用,“”表示空串,串长为0
- “ ”空格串不是空串,串长为空格数。
2)串的存储类型:顺序存储:定长顺序存储结构、堆分配存储结构(动态分配连续内存)
链式存储:
2、模式匹配——BP算法和KMP算法
串的模式匹配:就是指定一个主串S和子串T,求T在S中第一次出现的位置。
(1)朴素模式匹配(BP算法)
核心思想:主串S和子串T,S[1]和T[1]比较,S[2]与T[2]比较,直到S[n]与T[n]都是相等的为止,若S[i]与T[i]不相等,则子串向右移动一个(一个字符)继续比较。
时间复杂度:主串S长度为n,子串T长度为m,最多进行m(m-n+1)次,最坏的时间复杂度为O(mn) 效率不高。
(2)KMP算法
核心思想:尽量利用已经得到的“部分匹配”的结果信息,不要让i回溯,加快子串右滑的速度,问题由子串决定而不是主串决定的
next数组:是一个智能数组,指导子串下一步改用几号元素去匹配,next数组的next[1]和next[2]永远为0和1,其余next[n]看n位的前后缀的相同数目
实例:a、现有子串i=“ABABAAABA”,求其next数组
i | 9 | a | b | a | b | a | a | a | b | a |
j | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
next |
| 0 | 1 | 1 | 2 | 3 | 4 | 2 | 2 | 3 |
Next数组中对应i数组的值 | 前缀 | 后缀 | Next[]的值 |
Next[j1] | 0 | 0 | 0 |
Next[j2] | A | A | 1 |
Next[j3] | A | B | 1 |
Next[j4] | A | A | 2 |
Next[j5] | AB | AB | 3 |
Next[j6] | ABA | ABA | 4 |
Next[j7] | A | A | 2 |
Next[j8] | A | A | 2 |
Next[j9] | AB | AB | 3 |
b、实例,不详细解释,主串是S=“bbsbbcdfbb”子串是“bbsbbs”,求next数组?
T | 6 | b | b | s | b | b | s |
J | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
next |
| 0 | 1 | 2 | 1 | 2 | 3 |
c、主串是S=“sssssvsd”,子串是T=“ssssb”求next数组?
S | 5 | s | s | s | s | B |
l | 0 | 1 | 2 | 3 | 4 | 5 |
Next |
| 0 | 1 | 2 | 3 | 4 |
Next数组中对应i数组的值 | 前缀 | 后缀 | Next[]的值 |
Next[j1] | 0 | 0 | 0 |
Next[j2] | s | s | 1 |
Next[j3] | s | s | 2 |
Next[j4] | ss | ss | 3 |
Next[j5] | sss | sss | 4 |
KMP算法的时间复杂度:O(m+n)
3、串的操作代码(C语言)
#include <stdio.h>
#include <malloc.h>
#include <string.h>
typedef struct
{
char *ch; //定义一个数组来存放串 char ch[max](有最大的长度)等价于 char *ch (空串时指向NULL,非空串时按串长分配存储区)
int len; //定义串的长度
}PSeqString;
int initialstring(PSeqString *t) //为串初始化,初始化的目的是确保串能都得到正确的空间分配,
{
t->ch=NULL;
t->len=0;
return 1;
}
int assign(PSeqString *t,char *p) //把字符串P的值赋给串PSeqString t char *p想打昂与char p[]
{
int i,length=strlen(p);
if(t->ch) //如果字符串t已有内容则释放t的内容
free(t->ch);
if(!length) //判断赋值的 p[]数组是否为空,为空的话就直接吧串赋值为NULL;len=0
{
t->ch=NULL;
t->len=0;
return 0;
}
else
{
t->ch=(char *)malloc (length *sizeof(char)); //为每一个字符串申请长度 要乘以字符串的长度length 然后强制转换为char 类型 返回给一个指针
if(!t->ch)
return 0;
for(i=0;i<length; ++i)
{
t->ch[i]=p[i];
}
t->len=length; // 赋值t->len
return 1;
}
}
void prinfstring(PSeqString T) //循环遍历 输出串T
{
int i;
for(i=0;i<T.len;++i)
{
printf("%c",T.ch[i]);
}
printf("\n");
}
int stringInsert (PSeqString *s,int pos,PSeqString t) //在串s中下标为pos的字符之前插入串t;
{
int i;
char *temp;
if(pos<0 || pos>s->len||s->len==0) return 0; //插入位置不合法
temp=(char*)malloc (s->len+t.len);
if(temp==NULL) return 0;
for(i=0;i<pos ;i++)
temp[i]=s->ch[i];
for(i=0;i<t.len;i++)
temp[i+pos]=t.ch[i];
for(i=pos;i<s->len;i++)
temp[i+t.len]=s->ch[i];
s->len+=t.len ;
free(s->ch);
s->ch=temp;
return 1;
}
int addstring(PSeqString *T ,PSeqString s1,PSeqString s2)//将字符串s1 s2连接
{
int i;
if(T->ch)
free(T->ch);
T->ch=(char *)malloc ((s1.len+s2.len)*sizeof(char));
if(!T->ch)
return 0;
for (i=0;i<s1.len;++i) //先把s1的值赋值给T,再把s2的值赋给T
T->ch[i]=s1.ch[i];
for(i=0;i<s2.len;++i)
T->ch[i+s1.len]=s2.ch[i];
T->len=s1.len+s2.len;
return 1;
}
int main()
{
PSeqString s,t,T;
int i,pos=1;
char *p="asc";
char *q="jkb";
initialstring(&t);
initialstring(&s);
assign(&t,p);
assign(&s,q);
prinfstring(t);
prinfstring(s);
stringInsert (&t,pos,s);
prinfstring(t);
addstring(&T ,s,t);
prinfstring(T);
}
运行结果如下: