字符串

1.串类型的定义

  • 串是由零个或多个字符组成的有限序列
  • 串中任意个连续的字符组成的子序列称为该串的子串。包含子串的串相应地称为主串。子串在主串中的位置则以子串的第一个字符在主串中的位置来表示
  • 串赋值、串比较、求串长和求子串、串连接以及求子串五种操作构成串类型的最小操作子集,即这些操作不可能利用其它串操作来实现。

2.串的表示和实现

(1)定长顺序存储表示

  • 定义:
#define MAXSTRLEN 255
typedef unsigned char SString[MAXSTRLEN+1];  //0号单元存放串的长度
  • 串连接的实现
bool Concat(SString& T, const SString& S1, const SString& S2)
{
	//如果未截断
	if (S1[0] + S2[0]<=MAXSTRLEN)
	{
		int k = 1;
		for (int i = 1 ;i <= len(S1) ; ++i)
		{
			T[k++] = S1[i];
		}
		for (int i = 1; i <= len(S2); ++i)
		{
			T[k++] = S2[i];
		}
		T[0] = S1[0] + S2[0];
	}
	//截断1
	if (S1[0] < MAXSTRLEN)
	{
		int k = 1;
		for (int i = 1; i <= len(S1); ++i)
		{
			T[k++] = S1[i];
		}
		for (int i = 1; i <= MAXSTRLEN-len(S1); ++i)
		{
			T[k++] = S2[i];
		}
		T[0] = S1[0] + S2[0];
		return false;
	}
	else
	{
		for (int i = 0; i <= len(S1); ++i)
		{
			T[i] = S1[i];
		}
		return false;
	}
	return true;
}
  • 求子串
bool SubString(SString& sub,SString s,int pos,int len)
{
bool SubString(SString& sub, SString s, int pos, int len)
{
	if (pos<1 || pos>s[0] || len<0 || len>s[0] - pos)
		return false;
	else
	{
		int k = 1;
		for (int i = pos; i <= len + pos; ++i)
		{
			sub[k] = s[i];
		}
		sub[0] = len;
	}
	return true;
}
}

(2)堆分配存储表示

  • 即使用堆动态分配串的长度,高级语言中经常使用该方法
typedef struct
{
	char* ch;
	int length;  
}SString;
  • 串插入操作
bool StrInsert(SString& s, int pos, SString T)
{
	if (pos < 1 || pos + s.length + 1) return false;
	if (T.length)  //T非空,则重新分配空间
	{
		s.ch = (char*)realloc(s.ch,(s.length+T.length)*sizeof(char));
		if (!s.ch) return false;
		for (int i = pos-1; i < s.length; ++i)
			s.ch[i + T.length] = s.ch[i];
		int k = 0;
		for (int i = pos - 1; i < pos - 1 + T.length; ++i)
		{
			s.ch[i] = T.ch[k++];
		}
		s.length += T.length;
	}
	return true;
}

(3)串的块链存储表示

  • 一个结点可以存放多个字符,通常链表中的最后一个结点不一定全被串值占满,此时通常补上“#”或其他的非串值字符
  • 除了头指针外还可附设一个尾指针指示链表中的最后一个结点,并给出当前串的长度,称如此定义的串存储结构为块链结构
  • 块链结构的定义
#define CHUNKSIZE 80
typedef struct Chunk
{
	char ch[CHUNKSIZE];
	struct Chunk *next;
}Chunk;
typdef struct{
	Chunk *head,*tail;
	int curlen;
}

3.串的模式匹配算法

  • 子串的定位操作通常称作串的模式匹配

(1)普通算法

在这里插入图片描述

int Index(SString S,SString T,int pos)
{
	i = pos;
	j = 1;
	while(1<=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;
}
  • 上述算法的优点是匹配过程简单容易理解,且在某些场合,如文本编辑等,效率也比较高,但是某些场合会出现效率特别低的情况,比如当模式串为"000000001",而主串为“0000000000000000000000000000000000000000000001”时,指针会回溯很多次。在最坏的情况下时间复杂度为O(n*m)

(2)KMP匹配算法

具体解法看这篇博客,讲的比较清楚

void GetNext(int next[],String t)
{
	int j = 0,k=-1;
	next[0]=-1;
	while(j<t.length-1)
	{
		if(k==-1 || t[j]==t[k])
		{
			j++;k++;
			if(t[j]==t[k])
				next[j]=next[k];
			else
				next[j]=k;
		}
		else
			k = next[k];
	}
}
int KMP(String s,String t)
{
	int next[MaxSize],i=0;j=0;
	Getnext(t,next);
	while(i<s.length&&j<t.length)
	{
		if(j==-1 || s[i]==t[j])
		{
			i++;
			j++;
		}
		else 
			j = next[j];
	}
	if(j>=t.length)
		return i-t.length;
	else
		return -1;
}

4.字符串hash

(1)将字符串转换成唯一整数

假设A—Z对应0—25,a—z对应26~51,0-9对应52-61,这样就把26个大写字母和数字对应到了六十二进制中,就可以实现字符串对应唯一整数

int hashFunc(char S[],int len)
{
	int id = 0;
	for(int i = 0;i<len;++i)
	{
		if(S[i]>='A'&&S[i]<=Z)
			id = id*62+(S[i]-'A');
		else if(S[i]>='a'&& S[i]<='z')
			id = id*62+(S[i]-'a')+26;
		else if(S[i]>='0'&&S[i]<='9')
			id = id*62+(S[i]-'0')+52;
	}
	return id;
}

(2)字符串hash进阶

由于按照62进制来hash字符串,当字符串较长时,产生的整数会非常大,为了应对这种情况,只能舍弃一些“唯一性”,将产生的结果对一个整数mod取模,但是这又产生另外的问题,也就是可能有多个字符串的hash值相同,导致冲突,不过幸运的是,在实践中发现,在int数据范围内,如果把进制数设置为一个107级别的素数p(例如10000019),同时把mod设置为一个109级别的素数(例如1000000007),那么冲突的概率将会变得非常小,很难产生冲突

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值