本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
1 串(String)的定义
串是由零个或多个字符组成的有限序列,又叫字符串。
一般记为,其中,s是串的名称,用双引号括起来的字符序列是串的值,可以是字母、数字或其他字符,就是该字符在串中的位置。
串中的字符数目称为串的长度,零个字符的串称为空串(null string)。所谓序列说明串的相邻字符之间具有前驱和后继的关系。
子串与主串,串中任意个数的连续字符组成的子序列称为该串的子串,相应地,包含子串的串称为主串。
2 串的比较
串的比较是通过组成串的字符之间的编码来进行的,而字符的编码指的是字符在对应字符集中的序号。
给定两个串:,,当且仅当时,且,,·····,,我们认为。
那么对于两个串不相等时,如何判定它们的大小呢?
给定两个串:,,当满足以下条件之一时,:
-
,且
例如当s=“hap”,t=“happy”,就有s<t。因为t比s多出了两个字母。
-
存在某个,且
例如当s=“happen”,t=“happy”,因为两串的前4个字母均相同,而两串第5个字母(k值),字母e的ASCII码是101,而字母y的ASCII码是121,显然e<y,所以s<t。
3 串的抽象数据类型
对于串的基本操作与线性表是有很大差别的。线性表更关注的是单个元素的操作,比如查找一个元素、插入或删除一个元素,但串中更多的是查找子串位置、得到指定位置子串、替换子串等操作。
串的伪代码定义如下:
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≤1en≤StrLength(S)-pos+1,用Sub返回串S的第 pos个字符起长度为1en的子串。
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)+1。从串S中删除第pos个字 符起长度为len的子串。
endADT
Index实现算法:
/* 返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数返回值为0。 */
/* 其中,T非空,1≤pos≤StrLength(S)。 */
int Index(String s, String T, int pos){
int n,m,i;
String sub;
if(pos>0){
n = StrLength(S); // 得到主串S的长度
m = StrLength(T); // 得到子串T的长度
i = pos;
while(i<=n-m+1){
SubString(sub,S,i,m); // 取子串第i个位置开始长度与T相等的字串给sub
if(StrCompare(sub,T) != 0){ // 如果两串不相等
++i;
}else{ // 如果两串相等
return i; // 返回i值
}
}
}
return 0; // 若无子串与T相等,返回0
}
StrLength实现算法:
/* 返回串的元素个数 */
int StrLength(String S)
{
return S[0];
}
SubString实现算法:
/* 用Sub返回串S的第pos个字符起长度为len的子串。 */
Status SubString(String Sub,String S,int pos,int len)
{
int i;
if(pos<1||pos>S[0]||len<0||len>S[0]-pos+1)
return ERROR;
for(i=1;i<=len;i++)
Sub[i]=S[pos+i-1];
Sub[0]=len;
return OK;
}
Strcompare实现算法:
/* 初始条件: 串S和T存在 */
/* 操作结果: 若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0 */
int StrCompare(String S,String T)
{
int i;
for(i=1;i<=S[0]&&i<=T[0];++i)
if(S[i]!=T[i])
return S[i]-T[i];
return S[0]-T[0];
}
4 朴素的模式匹配算法
子串的定位操作通常称做串的模式匹配,最重要的就是去找一个单词在一篇中的定位问题。
假设要从主串S=“goodgoogle”中,找到T=“google”这个子串的位置,下面是进行的步骤。
-
主串S第—位开始, S与T前三个字母都匹配成功,但S第四个字母是d,而T的是g。第一位匹配失败。
-
主串S第二位开始,主串S首字母是o,要匹配的T首字母是g,匹配失败。
-
主串S第三位开始,主串S首字母是o,要匹配的T首字母是g,匹配失败。
-
主串S第四位开始,主串S首字母是d,要匹配的T首字母是g,匹配失败。
-
主串S第五位开始,S与T,6个字母全匹配,匹配成功。
简单的说,就是对主串的每个字符作为子串开头,与要匹配的字符串进行匹配,对主串做大循环,每个字符开头做T的长度的小循环,直到匹配成功或全部遍历为止。
模式匹配算法Index:
/* 返回子串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[0] && 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;
}
}
时间复杂度: