Chapter 5 串
- 是什么:由零个或多个字符组成的有限序列,又叫字符串。
- 串中的字符数目n称为串的长度。零个字符的串称为空串。
- 空格串是只包含空格的串。注意它与空串的区别,有内容有长度,而且可以不止一个空格。
- 子串:串中任意个数的连续字符组成的子序列称为该串的子串,包含子串的串称为主串。子串在主串中的位置就是子串的第一个字符在主串中的序号。
- 串的比较是通过组成串的字符之间的编码来进行的。计算机中的常用字符是使用标准的ASCII编码,用7位二进制数表示一个字符,总共可表示128个字符。后来发现不够用,于是扩展ASCII码由8位二进制数表示一个字符,总共可以表示256个字符。其他语言用Unicode编码,比较常用的是由16位的二进制数表示一个字符,约是6.5万多个字符。为了和ASCII码建通,Unicode的前256个字符与ASCII码完全相同。
串的抽象数据类型
- 基本操作上,线性表更关注的是单个元素的操作,比如查找、删除、插入一个元素,但串中更多的是查找字串位置、得到指定位置子串、替换子串等操作。
ADT 串(string)
Data
串中元素仅由一个字符组成,相邻元素具有前驱和后继关系。
Operation
StrAssign(T,*chars): 生成一个其值等于字符串常量chars的串T。
StrCopy(T,S): 串S存在,由串S复制得串T。
ClearString(S): 串S存在,将串清空。
StringEmpty(S): 若串S为空,返回true,否则返回false
StrLength(S): 返回串S的元素个数,即串的长度。
StrCompare(S,T): 若S>T,返回值>0,若S=T,返回0,若S<T,返回值<0。
Concat(T,S1,S2): 用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个字符之后第一次出现的位置,否则返回0。
Replace(S,T,V): 串S、T和V存在,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
int Index(String S, String T, int pos)
{
int n,m,i;
String sub;
if(pos>0)
{
n=StrLength(S);
m=StrLength(T);
i=pos;
while(i<=n-m+1)
{
SubString(sub,S,i,m);
if(StrCompare(sub,T)!=0)
++i;
else
return i;
}
}
return 0;
}
串的存储结构
- 串的顺序存储结构:用一组地址来纳许的存储单元来存储串中的字符序列。一般用定长数组来定义,在后面加"\0"表示串的终结。
- 串的链式存储结构:与线性表类似,但如果一个结点放一个字符会存在很大浪费,因此一个结点可放多个字符,最后一个结点若是未被沾满,可用"#"或其他非串值字符补全。
朴素的模式匹配算法
int Index(String S, String T, int pos)
{
int i=pos;
int j=1;
while(i<=S[0] && j<=T[0])
{
if(S[i]==T[j])
{
++i;
++j;
}
else
{
i=i-j+2;
j=1;
}
if(j>T[0])
return i-T[0];
else
return 0;
}
}
KMP模式匹配算法
- 原理:i值不回溯。T串的首字符与后面字符比较,如果发现有相等字符,j值的变化就会不相同。也就是说,j值的变化与主串其实没什么关系,关键取决于T串的结构中是否有重复的问题。把T串各个位置的j值的变化定义为next数组,next数组的长度就是T串的长度。
定义(j从1开始):
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/864bb52fb5ed441db5660672a3b71c6f.png)
- next数组值推导:
方法①:next[1]=0;next[2]=1;从3开始,计算next[j]看1到 j-1的子串,next[j]=子串前缀与后缀相同字符个数+1(没有相同的时候就为1);
方法②:得到T[j-1]的字符,不断顺着next[]往前找,找到第一个相等的T[i],next[j]=next[i递归的上一个]+1;若找到下标为 1 还不等,直接赋值 next[j] = 1。 - 算法实现:
void get_next(String T,int *next)
{
itn i,j;
i=1;
j=0;
next[1]=0;
while(i<T[0])
{
if(j==0 || T[i]==T[j])
{
++i;
++j;
next[i]=j;
}
else
j=next[j];
}
}
int Index_KMP(String S, String T,int pos)
{
int i=pos;
int j=1;
int next[255];
get_next(T,next);
while(i<=S[0] && j<=T[0])
{
if (j==0 || S[i]==T[i])
{
++i;
++j;
}
else
{
j=next[j];
}
}
if(j>T[0])
return i-T[0];
else
return 0;
}
- 算法改进:用首位next[1]的值取取代它相等的字符后续next[j]的值。
void get_nextval(String T, int *nextval)
{
int i,j;
i=1;
j=0;
nextval[1]=0;
while(i<T[0])
{
if(j==0 || T[i]==T[j])
{
++i;
++j;
if(T[i]!=T[j])
nextval[i]=j;
else
nextval[i]=nextval[j];
}
}
}
- nextval数组值推导:先计算处next数组,看当前j处的字符,查以next[j]下标的字符,若相等就用人家的next[]值,否则保持自己的next[]值。
总结:
- 串是线性表的扩展,对串这种结构更多的是关注它子串的应用问题。