串
(1)串的定义
串:是由零个或多个字符组成的有限序列,又名叫字符串
一般记为s=“a1a2a3…an”(n>=0),其中,s是串的名称,用双引号括起来的字符序列是串的值,引号不属于串的内容。ai(1<=i<=n)可以是字母、数字或其他字符。i为该字符在串中的位置。串中的字符数目n称为串的长度,n是一个有限的数值。零个字符的串称为空串,其长度为零,可以直接用**""**表示。序列说明串的相邻字符之间具有前驱后继关系。
空格串:只包含空格的串,空格串是有长度的,可以不止一个空格
子串与主串:串中任意个数的连续字符组成的子序列称为该串的子串,包含子串的串称为主串
子串在主串中的位置就是子串的第一个字符在主串中的序号
(2)串的比较
串的比较是通过组成串的字符之间的编码来进行的,而字符的编码指的是字符在对应字符集中的序号
字符串相等:
当两个串:s=“a1a2…an”,t=“b1b2…bm”,当且仅当n=m.且a1=b1,a2=b2,…,an=bm是,s=t
当字符串不相等时,比较大小:满足一下条件之一时,s<t
-
n<m,且ai=bi(i=1,2,…,n)
-
存在某个k<=min(m,n),使得ai=bi(i=1,2,…,k-1),ak<bk
(3)串的抽象数据类型
ADT 串(string)
Data
//元素仅由一个字符组成,相邻元素具有前驱和后继关系
Operation
StrAssign(t,*chars);//个其值等于字符串常量chars的串T
StrCopy(T,S);//串s存在,由串s复制得串T
ClearString(S);//串s存在,将串清空
StringEmpty(S);//若串为空,返回true,否则返回false
StrLength(S);//返回串的元素个数,即串的长度
StrCompare(S,T);//若S>T,返回值>0,若s=t,返回值0,若s<t返回值<0
Concat(T,s1,s1);//T返回由s1和s2联接而成的新串
SubString(Sub,S,pos,len);//串s存在,1<=pos<=StrLength(s),且0<=len<=StrLength(s)-pos+1,用Sub返回串s的第pos个字符起长度为len的子串
Index(S,T,pos);//串s和t存在,t是非空串,1<=pos<=StrLength(s).若主串s中存在和串t值相同的子串,则返回它在主串s中第pos个字符之后第一次出现的位置
Replace(s,t,v);//串s和t存在,t为非空串,用v替换主串s中出现的所有与t相等的不重叠的子串
StrInsert(S,pos,T);//串s和t存在,1<=pos<=StrLength(s)+1,在串S的第pos个字符之前插入串t
StrDelete(S,pos,len);//串s存在,1<=pos<=StrLength(s)-len+1.从串s中删除第pos个字符串起长度为len的子串
endADT
(4)串的存储结构
a.串的顺序存储结构
串的顺序存储结构是用一组地址连续的存储单元来存储串的字符序列的。按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区。一般用定长数组来定义。
b.串的链式存储结构
一个结点可以存放一个字符,也可以考虑存放多个字符,最后一个结点若是未被占满时,可以用“#”或其他非串值字符补全。
(5)朴素的模式匹配算法
串的模式匹配:子串的定位操作
对主串的每一个字符作为子串开头,与要匹配的字符串进行匹配。对主串做大循环,每个字符开头做T的长度的小循环,直到匹配成功或全部遍历完成为止。
假设主串S和要匹配的子串T的长度存在S[0]与T[0]中
//返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数返回值为0
//T非空,1<=pos<=StrLength(s)
int Index(String S,String T,int pos)
{
int i=pos;//i用于主串s中当前位置下标,若pos不为1,则从pos位置开始匹配
int j=1;//j用于子串T中当前位置下标值
while(i<=s[10]&&j<=T[0])//若i小于S长度且j小于T的长度时循环
{
if(s[i]==T[j])//两字母相等则继续
{
++i;
++j;
}
else//指针后退重新开始匹配
{
i=i-j+2;//i退回上次匹配首位的下一位
j=1;//j退回子串T的首位
}
}
if(j>T[0])
{
return i-T[0];
}
else
{
return 0;
}
}
(6)KMP模式匹配算法(克努特—莫里斯—普拉特算法)
主串S,匹配串T
把T串各个位置的j值的变化定义为一个数组next,next的长度就是T串的长度,得函数定义
- next[j]=0;当j=1时
- next[j]=Max{k|1<k<j,且“p1…p(k-1)"=“p(j-k+1)…p(j-1)”}当此集合不空时
- next[j]=1;其他情况
a.next数组值推导
例1
j | 123456 |
---|---|
模式串T | abcdex |
next[j] | 011111 |
- 当j=1时,next[1]=0
- 当j=2时,j由1到j-1只有字符”a",属于其他情况next[2]=1
- 当j=3时,j由1到j-1串是"ab",显然"a"与"b"不相等,属于其他请况next[3]=1
- 此后同理,T串的next[j]为011111
例2
j | 123456789 |
---|---|
模式串T | ababaaaba |
next[j] | 011234223 |
- 当j=1时,next[1]=0
- 当j=2时,j由1到j-1只有字符”a",属于其他情况next[2]=1
- 当j=3时,j由1到j-1串是"ab",显然"a"与"b"不相等,属于其他请况next[3]=1
- 当j=4时,j由1到j-1的串是"aba",前缀字符"a"与后缀字符"a"相等,next[4]=2
- 当j=5时,j由1到j-1的串是"abba",前缀字符"ab"与后缀字符"ab"相等,next[5]=3
- 当j=6时,j由1到j-1的串是"ababa",前缀字符"aba"与后缀字符"aba"相等,next[6]=4
- 当j=7时,j由1到j-1的串是"ababaa",前缀字符"ab"与后缀字符"aa"相等,next[7]=2
- 当j=8时,j由1到j-1的串是"ababaaa",只有"a"相等,next[8]=2
- 当j=9时,j由1到j-1的串是"ababaaab",前缀字符"ab"与后缀字符"ab"相等,next[9]=3
b.KMP模式匹配算法实现
//通过计算返回子串T的next数组
void get_next(String T,int *next)
{
int i,j;
i=1;
j=0;
next[1]=0;
while(i<T[0])//此处T[0]表示串T的长度
{
if(j==0||T[i]==T[j])//T[i]表示后缀的单个字符,T[j]表示前缀的单个字符
{
++i;
++j;
next[i]=j;
}
else
{
j=next[j];//若字符不相同,则j值返回溯
}
}
}
//返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数返回值为0
//T非空,1<=pos<=StrLength(s)
int Index(String S,String T,int pos)
{
int i=pos;//i用于主串s中当前位置下标,若pos不为1,则从pos位置开始匹配
int j=1;//j用于子串T中当前位置下标值
int next[255];//定义一个next数组
get_next(T,next);//对串T作分析,得next数组
while(i<=s[10]&&j<=T[0])//若i小于S长度且j小于T的长度时循环
{
if(j==0||s[i]==T[j])//两字母相等则继续
{
++i;
++j;
}
else//指针后退重新开始匹配
{
j=next[j];//j退回合适的位置,i值不变
}
}
if(j>T[0])
{
return i-T[0];
}
else
{
return 0;
}
}
c.KMP模式匹配算法改进
取代数组nextval
//求模式串T的next函数修正值并存入数组nextval
void get_nextval(String T,int *nextval)
{
int i,j;
i=1;
j=0;
nextval[1]=0;
while(i<T[0])//此处T[0]表示串T的长度
{
if(j==0||T[i]==T[j])//T[i]表示后缀的单个字符,T[j]表示前缀的单个字符
{
++i;
++j;
if(T[i]!=T[j])//若当前字符与前缀字符不同
{
nextval[i]=j;//则当前j为nextval在i位置的值
}
else
{
nextval[i]=nextval[j];//如果与前缀字符相同,值赋给nextval在nextval在i位置的值
}
else
{
j=nextval[j];//若字符不相同,则j值回溯
}
}
}
}
d.nextval数组值推导
j | 123456789 |
---|---|
模式串T | ababaaaba |
next[j] | 011234223 |
nextval[j] | 010104210 |
- 当j=1时,nextval[1]=0
- 当j=2时,因第二位字符"b"的next值是1,而第一位就是"a",它们不相等,所有nextval[2]=next[2]=1,维持原值
- 当j=3时,因为第三位字符"a"的next值为1,所以与第一位的"a"比较得知它们相等,所以nextval[3]=nextval[1]=0
- 当j=4时,因为第四位字符"b"的next值为2,所以与第二位的"b"比较得知它们相等,所以nextval[4]=nextval[2]=1
- 当j=5时,next值为3,因为第五位字符"a"与第三位的"a"比较得知它们相等,所以nextval[5]=nextval[3]=0
- 当j=6时,next值为4,因为第六位字符"a"与第四位的"b"比较得知它们不相等,所以nextval[6]=4
- 当j=7时,next值为2,因为第七位字符"a"与第二位的"b"比较得知它们不相等,所以nextval[7]=2
- 当j=8时,next值为3,因为第九位字符"a"与第三位的"a"比较得知它们相等,所以nextval[9]=nextval[3]=0
- 当j=9时,next的值为3,第九个字符"a"与第三个字符“a"相等,因此nextval[9]=nextval[3]=0