`
java-mans
  • 浏览: 11440651 次
文章分类
社区版块
存档分类
最新评论

字符串

 
阅读更多

其实字符串就是一种更加特殊的线性结构。他表现在:1.使用char类型的数组来存储。2.面向字符串的操作与一般的顺序表不同,比如元素的插入、删除、确定位置等,一般的顺序表定义的操作都是1次处理一个元素,而字符串的操作往往是处理若干个字符。

因为字符串操作的这些特殊性,所以C语言中专门在string.h头文件中定义了许多关于字符串的操作,其中比较重要的是:字符串输出函数 puts、字符串输入函数gets、字符串连接函数strcat、字符串拷贝函数strcpy、字符串比较函数strcmp、测字符串长度函数strlen等等。我们这里在不使用标准库的情况下,自己动手写一些与这些函数功能类似的函数。

首先考虑字符串的定义,按照我以前的风格,都不是通过定义一个很长的数组来实现,而是通过动态分配内存的方法来实现,这样的话,需要一个变量记录动态数组的长度,有了这个长度,我们就不需要像C语言那样通过在结尾加添'\0'来判断字符串是否结束了。

typedef struct String
{
	char* data;
	int size;
}String;

//分配字符串
bool assignStr(String *pT,char *chars)
{


	//求字符串的长度
	int i = 0;
	while(chars[i] != '\0')
		++i;


	//如果是空串
	if(0 == i)
	{
		pT->data = NULL;
		pT->size = 0;
	}
	else
	{
		pT->size = i;
		pT->data = (char*)malloc(sizeof(char) * pT->size);
		if(NULL ==pT->data )
			return false;
		else
		{
			for(int j = 0; j < i;++j)
				pT->data[j] = chars[j];
			
		}
	}
	return true;
}

//销毁字符串
void clearStr(String *pS)
{	
	free(pS->data);
	pS->data = NULL;
	pS->size  = 0;
}

//求字符串长度
int strLength(String *pS)
{
	return pS->size;
}

//字符串拷贝
bool strCpy(String *pT,String *pS)
{
	if(strLength(pT) < strLength(pS))
	{
		pT->data = (char*)realloc(pT->data,sizeof(char) * pS->size);
		pT->size = pS->size;
	}
	if(NULL == pT->data)
		return false;
	else
	{
		for(int i = 0; i < strLength(pT);++i)
			pT->data[i] = pS->data[i];
		pT->size = pS->size;
		return true;
	}

}

//判断是否为空
bool is_empty(String *pS)
{
	if(NULL == pS->data && 0 == pS->size)
	{
		printf("empty");
		return true;
	}
	else
		return false;
}

//字符串比较
int strCmp(String *pT,String *pS)
{
	int i = strLength(pT);
	int j = strLength(pS);
	int k;
	//找出长度较短的字符串
	i < j ? k = i : k = j;
	for(int i = 0; i< k; ++i)
	{
		if(pT->data[i] > pS->data[i])
			return 1;
		else if(pT->data[i] < pS->data[i])
			return -1;
	}
	//如果程序能完整执行完for循环,说明两个字符串的前k个字符相等
	//剩下只要比较它俩谁长就可以了
	if(i < j)
		return -1;
	else if(i > j)
		return 1;
	else
		return 0;

}

 //将第二个字符串连接到第一个后面
void strCat(String *pT,String *pS)
{
	int i = strLength(pT);
	int j = strLength(pS);
	pT->data = (char*)realloc(pT->data,sizeof(char) * (i+j));
	for(int m = i,n = 0 ; m < (i+j); ++m,++n)
		pT->data[m] = pS->data[n];
	pT->size = i + j;
	
}
 //从指定的位置返回指定长度的子串
bool getSubString(String *pS,int pos,int len,String *pSubString)
{
	//判断输入的位置和长度是否合法
	if(pos < 0 ||  len <= 0 || len+pos > strLength(pS))
		return false;
	for(int i = pos,j = 0; i<pos+len;++i,++j)
	{
		pSubString->data[j] = pS->data[i];
	}
	pSubString->size = len;
	return true;
}

 //在指定位置后插入字符串
bool insertStr(String *pT,String *pS,int pos)
{
	
	int i = strLength(pT);
	int j = strLength(pS);
	if(pos < 0 || pos > j)
		return false;
	pS->data = (char*)realloc(pS->data,sizeof(char) * (i+j));
	pS->size = i+j;
	if(NULL == pS->data)
		return false;
	for(int m = i+j ; m > pos; --m)
		pS->data[m] = pS->data[m-i];

	for(int n = pos,l = 0; n < i+pos;++n,++l)
		pS->data[n] = pT->data[l];
	return true;
}

//删除指定位置后面的若干个字符串
bool deleteStr(String *pS,int pos,int len)
{
	int j = strLength(pS);
	if(pos < 0 || pos > j || len + pos > j)
		return false;
	for(int i = pos; i < j-len;++i)
		pS->data[i] = pS->data[i+len];
	pS->size -= len; 
	return false;
}


//检索:源字符串的某个位置以后是否有目标字符串有则返回位置,无则返回-1
int index(String *pT,String *pS,int pos)
{
	int m = strLength(pS);
	int n = strLength(pT);
	//如果比较位置大于等于源字符串的长度,返回-1
	if(pos >= m)
		return -1;
	int i = pos,j = 0;
	//当两个字符都没有访问到最后一个元素时,继续	
	while(i + n < m && j < n)
	{
		
		//如果从某一个位置起二者相等,继续比较
		if(pS->data[i] == pT->data[j])
		{
			++i;
			++j;
		}
		//否则源字符串从第一个字符匹配的下一个位置开始继续,目标字符串从头开始继续
		else
		{
			i = i - j+1 ;
			j = 0;
		}
		//如果目标串已经遍历完,说明找到了
		if(j == strLength(pT))
			return i- strLength(pT);
	}
	//没有找到,返回-1
	return -1;
}
//替换
void replace(String *pT,String *pS,String *pNew)
{
	//使用已经定义的操作完成:查找、删除、插入
	int m = strLength(pT);
	int n = strLength(pNew);
	int i = 0;
	do{
		i = index(pT,pS,i); 
		if(i != -1)
		{
			deleteStr(pS,i,m);
			insertStr(pNew,pS,i);
			i += n;			
		}
	}
	while(i != -1);
}

//打印字符串,调试时使用
void printStr(String *pT)
{
	printf("word: ");
//	不能使用下面这句,因为pT->data之前可能曾经很长
//	printf("%s",pT->data);
	for(int i = 0 ; i < strLength(pT);++i)
		printf("%c",pT->data[i]);
	printf("\n");
}


需要注意的是,字符串的索引操作中,有一种更加有效率的办法,称为:KMP算法,这里并没有使用,而是使用的更加直观的索引算法。

下面通过一个复杂的例子来结束对字符串的讨论:求两个字符串的最长公共子串。

//查找最长的公共子串,返回这个子串的位置,子串从pNew中带出
//算法简述:
//1.将两个子串组成一个矩阵,若矩阵行、列对应的元素相同,这个元素为1,反之为0
//2.遍历对角线
//3.找出长度最大的非0对角线,这条对角线对应的元素就是最长的公共子串
//举例
/*
	a	b	d	c	a	b	c	d
d	0	0	1	0	0	0	0	1
c	0	0	0	1	0	0	1	0
a	1	0	0	0	1	0	0	0
b	0	1	0	0	0	1	0	0
c	0	0	0	1	0	0	1	0
c	0	0	0	1	0	0	1	0
a	1	0	0	0	1	0	0	0

则最长公共子串为cabc
*/

//函数声明
//对于矩阵的每一个元素,计算它的对角线为1的对角线的长度
//输入参数为:矩阵、当前点的行、列坐标,矩阵的长、宽
int getDiaLength(int **M,int m,int n,int row,int col);


int getLongestSubString(String *pS1,String *pS2,String *pR)
{	
	int row = strLength(pS1);
	int col = strLength(pS2);
	//动态分配2维数组m*n
	//先分配第一维:每个是int*类型的指针,共m个
	int **pM = (int**)malloc(sizeof(int*)*row);
	//为每个int*类型的指针分配n个int的空间
	for(int i = 0; i< row;++i)
		pM[i] = (int*)malloc(sizeof(int)*col);
	for(int i = 0; i < row ; ++i)
	{
		for(int j = 0; j < col ; ++j)
		{

			if(pS1->data[i] == pS2->data[j])
				pM[i][j] = 1;
			else
				pM[i][j] = 0;
		}
	}

	//打印矩阵结果
	for(int i = 0; i < row;++i)
	{
		for(int j = 0; j < col; ++j)
			printf("%d ",pM[i][j]);
		printf("\n");

	}
	printf("\n");
	//默认最长长度为0
	int length = 0;
	int k = 0;
	for(int i = 0;i < row;++i)
	{
		for(int j = 0; j < col;++j)
		{
			k =  getDiaLength(pM,i,j,row,col);
			if(length < k)
			{
				length = k;
				//标记这个元素
				pM[i][j] = length;
			}

		}
	}
	//打印矩阵结果
	for(int i = 0; i < row;++i)
	{
		for(int j = 0; j < col; ++j)
			printf("%d ",pM[i][j]);
		printf("\n");

	}
	char* pchar = (char*)malloc(sizeof(char)*length+1);
	for(int i = 0 ; i < row; ++i)
	{
		for(int j = 0; j <col; ++j)
			if(length == pM[i][j] )
			{
				for(int m = 0; m < length; ++m,++i)
				{
					pchar[m] = pS1->data[i];
				}
				//填充结尾标识符
				pchar[length] = '\0';
				//令循环条件不满足,这样就能跳出二重循环了
				i = row;
				j = col;
			}
	}
	//打印一下字符串
	printf("%s",pchar);
	printf("\n");
	//将这个字符串分配给pR指针
	assignStr(pR,pchar);

	//释放内存
	free(pchar);
	for(int i = 0;i < row;++i)
		free(pM[i]);
	free(pM);
	pM = NULL;
	return length;
}
int getDiaLength(int **M,int m,int n,int row,int col)
{
	int len = 0;	
	while(m<row && n<col)
	{
		if(1 == M[m][n] )
		{
			++m;
			++n;
			++len;
		}
		else
			break;
	}
	return len;
}


程序是我自己写的,略有点繁琐,但是基本要求还是达到了,中间的一些打印信息只是在调试的时候使用的,正常使用时可以关掉。

我不由得得吐一下槽,因为C语言中并没有C++类中的数据成员的概念,所以在getDiaLength函数中,我不得不传递了如此多的参数。如果有人有更好的办法,希望能告诉我。

再顺便说一句,在C++中,由于定义string这个类,所以很多字符串的操作可以通过这个类提供的函数更加安全的进行(使用类的程序员不必使用指针)。这种使用标准库而不是指针的风格,是现代C++程序设计所提倡的。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics