//虽然我觉得,我刚开始复习的这些,在库里其实有可以直接调用的,有现成的,但我们还需要明白这是一种思想,所以我静下心来好好体会了一下,做这些事,其实都是很有必要的。
串(字符串):是零个或多个字符组成的有限序列。记作: S=“a1a2a3…”,其中S是串名,ai(1≦i≦n)是单个,可以是字母、数字或其它字符。
串值:双引号括起来的字符序列是串值。
串长:串中所包含的字符个数称为该串的长度。
空串(空的字符串):长度为零的串称为空串,它不包含任何字符。
空格串(空白串):构成串的所有字符都是空格的串称为空白串。
注意:空串和空白串的不同,例如“ ”和“”分别表示长度为1的空白串和长度为0的空串。
子串(substring):串中任意个连续字符组成的子序列称为该串的子串,包含子串的串相应地称为主串。
子串的序号:将子串在主串中首次出现时的该子串的首字符对应在主串中的序号,称为子串在主串中的序号(或位置)。
串相等:如果两个串的串值相等(相同),称这两个串相等。换言之,只有当两个串的长度相等,且各个对应位置的字符都相同时才相等。
串在计算机中有三种存储方式:
1.定长顺序存储:将串定义成字符数组,利用串名可以直接访问串值。用这种表示方式。串的存储空间在编译时确定,其大小不能改变。
#define MAX_STRLEN 256
//存储结构定义
typedef struct{
char str[MAX_STRLEN];
int length;
}StringType;
//将两串连接的操作
bool StrConcat(StringType s,StringType t)
{
int i,j;
if(s.length+t.length>MAX_STRLEN)
return false;
for(int i=0;i<t.length;i++)
s.str[s.length+i]=t.str[i];
s.length+=t.length;
return true;
}
//求子串
bool SubString(StringType s,int pos,int len,StringType *sub)
{
int k,j;
if (pos<0||pos>=s.length||len<pos||pos>=s.length)
return false; /* 参数非法 */
sub->length=len-pos+1;//求得子串长度
for(j=0,k=pos;k<=len;k++,j++)
{
sub->str[j]=s.str[k];
}
return true;
}
2.串的堆分配存储表示
一个空间足够大且地址连续的存储空间(称为:“堆”)供串使用,可以使用C语言的动态存储分配函数malloc()和free()来管理。动态的,变长的。
typedef struct
{
char *ch;
int length;
} HString;
bool StrConcat(HString *T,HString *s1,Hstring *s2)
//用T返回由s1和s2连结成的串
{
int k,j,t_len;
if(T.ch)
free(T);//释放旧空间
t_len=s1->length+s2->length ;
if ((p=(char *)malloc(sizeof((char)*t_len))==NULL)
{
printf("系统空间不够,申请空间失败\n") ;
return false;
}
for (j=0;j<s1->length;j++)
T->ch[j]=s1->ch[j];//将串s复制到串T中
for (k=s1->length;k<T->length;k++)
T->ch[k]=s1->ch[k];//将串s2复制到串T中
free(s1->ch);//这种方式跟上一种最大的不同
free(s2->ch);
return true;
}
3.链式存储表示
串的链式存储结构和线性表的串的链式存储结构类似,采用单链表来存储串,结点的构成是:data域(存放字符),next域(存放下一结点的指针)
存在的问题及解决方法:若每个结点仅存放一个字符,则结点的指针域就非常多,为了节省存储空间,考虑串结构的特殊性,使每个结点存放若干字符,这种结构称为块链结构。
#define BLOCK_SIZE 4
typedef struct Blstrtype
{
char data[BLOCK_SIZE];
struct Blstrtype *next;
}BNODE;
typedef struct
{
BNODE head;//头指针
int Strlen ;//当前长度
}Blstring;
/*当一个块(结点)内存放多个字符时,往往会使操作过程变得较为复杂,如在串中插入或删除字符操作时通常需要在块间移动字符。*/
最重要串的模式匹配
BF算法
int BFindex(SString S,SString P,int pos)
{
if(pos<1||pos>S.length)
exit(ERROR);
int i=pos,j=1;//字符串从1开始
while(i<=S.length&&j<=P[0])
{
if(S[i]==P[j])
{
i++;
j++;
}
else
{
i=i-j+2;
j=1;
}
}
if(j>P.length)
return i-P.length;
return ERROR;
}
该算法的时间复杂度为O(n*m) ,其中n 、m分别是主串和模式串的长度。通常情况下,实际运行过程中,该算法的执行时间近似于O(n+m) 。
KMP算法
这个改进算法在于:当每一趟匹配过程出现字符不相等时,主串指示器不用回溯,而是利用已经得到的“部分匹配”的结果,将模式串的指示器向右“滑动”尽可能远的一段距离后(j取较大的值)继续进行比较。
将si与pj的比较变为si与pk(可能不用从1开始)的比较(k<j)
当si≠pj(1≦i≦n-m,1≦j<m,m<n)时,主串s的指针i不必回溯,而模式串p的指针j回溯到第k(k<j)个字符继续比较,则模式串p的前k-1个字符必须满足4-1式,而且不可能存在k’>k满足4-1式。
p1p2…pk-1= si-(k-1) si-(k-2) … si-2 si-1 (4-1)
而已经得到的 “部分匹配”的结果为:
pj-(k-1) pj-k+2… pj-1=si-(k-1) si-(k-2) … si-2 si-1 (4-2)
由式(4-1)和式(4-2)得:
p1p2…pk-1=pj-(k-1) pj-k+2… pj-1 (4-3)
实际上,式(4-3)描述了模式串中存在相互重叠的子串的情况。
定义next[j]函数为
//我感觉这是我学kmp到现在最理解kmp算法的一次,哈哈哈,越看越明白
举个例子就是这样的,用上面那个公式其实很好理解
所以,我们的首要任务就是求出next数组,由next[j]的公式可知,求模式串的next[j]值与主串s无关,只与模式串p本身的构成有关:
当j=1时,next[1]=0
如果存在1<k<j,即next[j]=k,此时求next[j+1]的值有两种情况:
(1)若有pk=pj,则表明在模式串中有:p1p2…pk-1pk=pj-(k-1)pj-k+2… pj-1pj,即next[j+1]=next[j]+1=k+1
(2)若有pk≠pj,我们就开始找稍微短一点的重叠串,若next[k]= k’,且pj = pk’则说明在主串中第j+1字符之前存在一个长度为k’(即next[k])的最长子串,next[j+1]=k’+1
//next[j]的值是前1-j-1的最长重叠子串+1//也就是该比较的位置
//画个图以后特别明显,可以试着画一下,这其实就成了一个循环。
如果最后不存在任何k’(1< k’<j)满足等式:p1 p2…pk’-1 pk’=pj-(k’-1) pj-k’+2… pj-1 pj,则next[j+1]=1
void getnext(char p[],int nex[])
{
int i=1,j=0;
nex[1]=0;
while(i<strlen(p))
{
if(j==0||p[i]==p[j])//j=next[i]
{
i++;
j++;
nex[i]=j;
}
else
{
j=nex[j];
}
}
}
在求得了next数组之后,KMP的思想是:
设目标串为s,模式串为p,并设i指针和j指针分别指示目标串和模式串中正待比较的字符,设i和j的初值均为1。
若有si=pj,则i和j分别加1。否则,i不变,j退回到j=next[j]的位置,再比较si和pj,若相等,则i和j分别加1。否则,i不变,j再次退回到j=next[j]的位置,依此类推。直到下列两种可能:
(1)j退回到某个下一个next[j]值时字符比较相等,则指针各自加1继续进行匹配。
(2)退回到j=0,将i和j分别加1,即从主串的下一个字符si+1模式串的p1重新开始匹配。
int KMPindex(string S,string P,int pos)
{
if(pos<1||pos>S.length())
exit(ERROR);
int i=pos,j=1;
while(i<=S.length()&&j<=P.length())
{
if(j==0||S[i]==P[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
if(j>P.length())
return i-P.length();
return ERROR;
}
KMP算法仅当模式与主串之间存在许多“部分匹配”的情况下才显得比BF算法快得多。
来源:CSDN
作者:YoRoll_町
链接:https://blog.csdn.net/Gragon_sgirlfriend/article/details/104592148