文章目录

1. 串的基本概念2. 串的基本操作3. 串的存储实现4. 串的朴素模式匹配5. KPM算法

1. 串的基本概念

串:即字符串(String)是由零个或多个字符组成的有限序列。串的长度:中字符的个数 n,n = 0 n = 0n=0 时的串称为空串。子串:串中任意个连续的字符组成的子序列。主串:包含子串的串。字符在主串中的位置:字符在串中的序号。子串在主串中的位置:子串的第一个字符在主串中的位置 。

2. 串的基本操作

StrAssign(&T, chars):赋值操作。把串 T 赋值为 chars。StrCopy(&T, S):复制操作。由串 S 复制得到串 T。StrEmpty(S):判空操作。若 S 为空串,则返回 TRUE,否则返回 FALSE。StrLength(S):求串长。返回串 S 中元素的个数。ClearString(&S):清空操作。将 S 清为空串。DestroyString(&S):销毁串。将串 S 销毁(回收存储空间)。Concat(&T, S1, S2):串联接。用 T 返回由 S1 和 S2 联接而成的新串 。SubString(&Sub, S, pos, len):求子串。用 Sub 返回串 S 的第 pos 个字符起长度为 len 的子串。Index(S, T):定位操作。若主串 S 中存在与串 T 值相同的子串,则返回它在主串 S 中第一次出现的位置;否则函数值为 0。StrCompare(S, T):比较操作。若 S>T,则返回值>0;若 S=T,则返回值=0;若 S

3. 串的存储实现

串的顺序存储(静态数组):

// ch[0]废弃不用,声明int型变量length来存放串的长度

#define MAXLEN 255

typedef struct{

char ch[MAXLEN];

int length;

}SString;

// 串的初始化

bool InitString(SString &S){

S.length = 0;

return true;

}

// 求串的长度

int StrLength(SString S){

return S.length;

}

// 求主串由位序pos开始len长度的子串存入到串Sub中

bool SubString(SString &Sub, SString S, int pos, int len){

if(pos+len-1 > S.length)

return false;

for(int i=pos; i

Sub.ch[i-pos+1] = S.ch[i];

Sub.length = len;

return true;

}

// 比较S与T的大小。若S>T则返回值大于0;若S=T则返回值等于0;若S

int StrCompare(SString S, SString T){

for(int i=1; i<=S.length && i<=T.length; i++){

if(S.ch[i]!=T.ch[i])

return S.ch[i]-T.ch[i]

}

// 扫描过的所有字符都相同,则长度长的串更大

return S.length-T.length;

}

// 定位串T在串S中的位置,若无法定位则返回0

int Index(SString S, SString T){

int i=1, n=StrLength(S), m=StrLength(T);

SString sub; //用于暂存数据

while(i<=n-m+1){

SubString(sub, S, i, m);

if(StrCompare(sub, T)!=0)

++i;

else

return i;

}

return 0;

}

void test{

SString S;

InitString(S);

...

}

串的顺序存储(动态数组):

#define MAXLEN 255

typedef struct{

char *ch;

int length;

}HString;

bool InitString(HString &S){

S.ch = (char *)malloc(MAXLEN * sizeof(char));

if(S.ch == NULL)

return false;

S.length = 0;

return true;

}

void test{

HString S;

InitString(S);

...

}

串的链式存储:

typedef struct StringNode{

char ch; //每个结点存1个字符

struct StringNode *next;

}StringNode, *String;

上述方式存储密度很低,可以使每个结点存储多个字符。每个结点被称为块,整个链表被称为块链结构。

typedef struct StringNode{

char ch[4]; //每个结点存多个字符

struct StringNode *next;

}StringNode, *String;

4. 串的朴素模式匹配

串的模式匹配:在主串中找到与模式串相同的子串,并返回其所在主串中的位置。朴素模式匹配算法思想:将主串中与模式串长度相同的子串搞出来,挨个与模式串对比。当子串与模式串某个对应字符不匹配时,就立即放弃当前子串,转而检索下一个子串。串的朴素模式匹配算法代码实现:

// 在主串S中找到与模式串T相同的子串并返回其位序,否则返回0

int Index(SString S, SString T){

int k=1;

int i=k, j=1;

while(i<=S.length && j<=T.length){

if(S.ch[i] == T.ch[j]){

++i; ++j;

}else{

k++; i=k; j=1;

}

}

if(j>T.length)

return k;

else

return 0;

}

时间复杂度:设模式串长度为m,主串长度为n

匹配成功的最好时间复杂度:O ( m ) O(m)O(m)匹配失败的最好时间复杂度:O ( n ) O(n)O(n)最坏时间复杂度:O ( n m ) O(nm)O(nm)

5. KPM算法

朴素模式匹配算法的缺点:当某些子串与模式串能部分匹配时,主串的扫描指针 i 经常回溯,导致时间开销增加。最坏时间复杂度 O ( n m )串的前缀:包含第一个字符,且不包含最后一个字符的子串。串的后缀:包含最后一个字符,且不包含第一个字符的子串。next 数组的计算方法:当第 j 个字符匹配失败时,将前(1~j-1)个字符组成的串记为S,则:next[j]=S的最长相等前后缀长度+1。 特别地,next[1]=0

例:求模式串“abcabd”的 next 数组。

序号j123456模式串abcdefnext[j]011123

KPM 算法:当子串和模式串不匹配时,主串指针 i 不回溯,模式串指针 j=next[j]。KMP 算法的平均时间复杂度为:O ( n + m )KPM 算法代码实现:

// 获取模式串T的next[]数组

void getNext(SString T, int next[]){

int i=1, j=0;

next[1]=0;

while(i

if(j==0 || T.ch[1]==T.ch[j]){

++i; ++j;

next[i]=j;

}else

j=next[j];

}

}

// KPM算法,求主串S中模式串T的位序,没有则返回0

int Index_KPM(SString S, SString T){

int i=1, j=1;

int next[T.length+1];

getNext(T, next);

while(i<=S.length && j<=T.length){

if(j==0 || S.ch[i]==T.ch[j]){

++i; ++j;

}else

j=next[j];

}

if(j>T.length)

return i-T.length;

else

return 0;

}

int main() {

SString S={"ababcabcd", 9};

SString T={"bcd", 3};

printf("%d ", Index_KPM(S, T)); //输出9

}

KPM 算法的进一步优化:改进 next 数组:

void getNextval(SString T, int nextval[]){

int i=1,j=0;

nextval[1]=0;

while(i

if(j==0 || T.ch[i]==T.ch[j]){

++i; ++j;

if(T.ch[i]!=T.ch[j])

nextval[i]=j;

else

nextval[i]=nextval[j];

}else

j=nextval[j];

}

}

推荐文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。