2

串的定义及朴素的模式匹配算法

 2 years ago
source link: https://blog.51cto.com/Laccoliths/5666261
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1 串(String)的定义

早先在计算机在被发明时,主要作用是做一些科学和工程的工作,也就是类似于现在的计算器,只不过是比计算器功能更强大,速度更快一些。但是后来随着社会和技术的发展,在计算机上作废树脂处理的工作越来越多,使得我们不得不引入对字符的处理,于是就有了我们今天字符串的概念。

串的定义:是由零个或多个字符组成的有限序列,又叫字符串。

一般记为s=”a1a2⋅⋅⋅an(n≥0)"s=”a_1a_2···a_n (n\geq0)"s=”a1​a2​⋅⋅⋅an​(n≥0)",其中,s是串的名称,用双引号括起来的字符序列是串的值,引号不属于串的内容。ai(a_i(ai​( 1≤i≤n)1 \leq i \leq n )1≤i≤n)可以是字母、数字或其他字符,iii就是该字符在串中的位置。

串中的字符数目nnn称为串的长度,零个字符的串称为空串 (null string)。所谓序列说明串的相邻字符之间具有前驱和后继的关系。

还有一些特殊的串:

  1. 空格串:只包含空格的串,注意它与空串的区别,空格串是有内容有长度的,而且可以不止一个空格。
  2. 子串与主串,串中任意个数的连续字符组成的子序列称为该串的子串,相应地,包含子串的串称为主串。子串在主串中的位置就是子串的第一个字符在主串中的序号。

2 串的比较

串的比较是通过组成串的字符之间的编码来进行的,而字符的编码指的是字符在对应字符集中的序号。

给定两个串:s=”a1a2⋅⋅⋅an(n≥0)"s=”a_1a_2···a_n (n\geq0)"s=”a1​a2​⋅⋅⋅an​(n≥0)",t=”b1b2⋅⋅⋅bm(m≥0)"t=”b_1b_2···b_m (m\geq0)"t=”b1​b2​⋅⋅⋅bm​(m≥0)",当且仅当n=mn=mn=m时,且a1=b1a_1=b_1a1​=b1​,a2=b2a_2=b_2a2​=b2​,·····,an=bma_n=b_man​=bm​,我们认为s=ts=ts=t。

那么对于两个串不相等时,如何判定它们的大小呢?

给定两个串:s=”a1a2⋅⋅⋅an(n≥0)"s=”a_1a_2···a_n (n\geq0)"s=”a1​a2​⋅⋅⋅an​(n≥0)",t=”b1b2⋅⋅⋅bm(m≥0)"t=”b_1b_2···b_m (m\geq0)"t=”b1​b2​⋅⋅⋅bm​(m≥0)",当满足以下条件之一时,s≤ts\leq ts≤t:

  1. n<mn<mn<m,且ai=bi(i=1,2,⋅⋅⋅,n)a_i=b_i(i=1,2,···,n)ai​=bi​(i=1,2,⋅⋅⋅,n)

    例如当s=“hap”,t=“happy”,就有s<t。因为t比s多出了两个字母。

  2. 存在某个k≤min(m,n)k\leq min(m,n)k≤min(m,n),且ai=bi(i=1,2,⋅⋅⋅,k−1),ak<bka_i=b_i(i=1,2,···,k-1),a_k<b_kai​=bi​(i=1,2,⋅⋅⋅,k−1),ak​<bk​

    例如当s=“happen”,t=“happy”,因为两串的前4个字母均相同,而两串第5个字母(k值),字母e的ASCII码是101,而字母y的ASCII码是121,显然e<y,所以s<t。

比如“silly”和“stupid”在计算中的大下取决于它们挨个字母的前后顺序,它们的第一个字母是“s”,我们认为不存在大小差异,而第二个字母,由于“i”字母比“t”字母要考前,所以“i”<“t”,于是我们说“silly”小于“stupid”。

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”这个子串的位置,下面是进行的步骤。

  1. 主串S第—位开始, S与T前三个字母都匹配成功,但S第四个字母是d,而T的是g。第一位匹配失败。

    串的定义及朴素的模式匹配算法_子串

  2. 主串S第二位开始,主串S首字母是o,要匹配的T首字母是g,匹配失败。

串的定义及朴素的模式匹配算法_字符串_02

  1. 主串S第三位开始,主串S首字母是o,要匹配的T首字母是g,匹配失败。

    串的定义及朴素的模式匹配算法_字符串_03

  2. 主串S第四位开始,主串S首字母是d,要匹配的T首字母是g,匹配失败。

    串的定义及朴素的模式匹配算法_串_04

  3. 主串S第五位开始,S与T,6个字母全匹配,匹配成功。

    串的定义及朴素的模式匹配算法_子串_05

简单的说,就是对主串的每个字符作为子串开头,与要匹配的字符串进行匹配,对主串做大循环,每个字符开头做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;
    }
}

时间复杂度:O((n−m+1)∗m)O((n-m+1)*m)O((n−m+1)∗m)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK