串是线性表的一种特殊情形,即当线性表的元素为字符型的情形,而对于串,往往要对其连续的字符组进行操作,故对其进行单独研究。
目录
串的表示
常见表示方法有以下三种:顺序表示、定长结点的链接表示、不定长结点的链接表示。这里总结了前两种。
1.串的顺序表示
静态数组实现(定长顺序存储):
define maxlength 100
typedef struct
{
char ch[maxlength];
int length;
}SString;
动态数组实现(堆分配存储):
define maxlength 100
typedef struct
{
char *ch;
int length;
}HString;
HString S;
S.ch=(char *)malloc(sizeof(char)*maxlength);//需要手动free
S.length=0;
2.串的链接表示
typedef struct StringNode
{
char ch;
struct StringNode *next;
}StringNode;
以上的结构一个结点含1B字符,4B指针,存储密度低,可进行如下改进:
typedef struct StringNode
{
char ch[4];
struct StringNode *next;
}StringNode;
这样每个结点可以存储4个字符,提高了存储效率。
模式匹配算法
串的模式匹配(pattern matching)即子串定位是一种重要的串操作。设s,t是给定的两个串。再主串s中找到子串t的过程称为模式匹配。
1.朴素的模式匹配方法——BF算法
BF算法整个流程其实就是对主串的每一个字符作为子串的开头,与要匹配的字符串进行匹配,然后对主串做大循环,对每个字符开头做T的长度的小循环,直到匹配成功或全部遍历完成为止。代码中采用静态数组实现(定长顺序存储)。
int Index_BF(SString *s,SString *t)
{
int i=1,j=1;
while(i<=s->length&&j<=t->length)//均未扫描结束
{
if(s->ch[i]==t->ch[j])
{
i++;
j++;
}//继续
else
{
i=i-j+2;//回溯
j=1;
if((s->length-i)<(t->length-j))
{
break;//剩余字符不够比较
}
}
if(j>t->length)
{
return (i-t->length);
}
}
return 0;
}
算法性能分析
设主串长度为n,模式串长度为m
匹配成功的最好时间复杂度 o(m)
匹配失败的最好时间复杂度 o(n-m+1)=o(n) (大多数情况n远远大于m)
最坏时间复杂度 o(m*n)
缺点:当某些子串与模式串部分匹配的时候,主串指针经常回溯,导致时间开销增加。
2.改进的模式匹配算法——KMP算法
KMP算法与BF算法的不同之处在于KMP算法中主串指针不回溯,模式串指针回溯,主要思想就是若模式串中存在相等的前后缀,那么上一次匹配的后缀部分等同于下一次匹配的前缀部分,此时直接从相等的前缀部分的下一个字符开始匹配(该字符位置存放于next数组中,仅和模式串有关)。
如何求next数组
串的前缀:包含第一个字符且不包含最后一个字符的子串
串的后缀:包含最后一个字符且不包含第一个字符的子串
第j个字符匹配失败,由前1~j-1个字符组成串记为S,则next[j]=S的最长相等前后缀长度+1
(构造该数组时间和空间复杂度均为 o(m))
例如:
序号j | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
模式串 | a | b | a | b | a | a |
next [ j ] | 0 | 1 | 1 | 2 | 3 | 4 |
具体归纳推导过程见张岩等主编数据结构与算法(第五版)P65-P67。
int Index_KMP(SString *s,SString *t)
{
int next[t->length];
int i=1,j=0;
next[1]=0;
while(i<t->length)
{
if(j==0||t->ch[i]==t->ch[j])
{
i++;
j++;
next[i]=j;
}
else
{
j=next[j];
}
}//求next数组
/*开始查找*/
i=1;
j=1;
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;
}
}
return 0;
}
Code::Blocks下测试结果:
算法性能分析
设主串长度为n,模式串长度为m
时间复杂度o (m+n)
此外,将next数组优化为nextval数组,可以进一步对算法进行优化,减少时间开销。