五、串

  • 【string】:由零个或多个字符组成的有限序列,又称字符串
  • 记为 s = “a1a2a3…an”(n>=0)
  • 空串:”” 或 Φ
  • 子串:主串的子序列
  • 子串在主串中的位置:第一个字符在主串中的序号

串的比较

s = “a1a2a3…an”,t = “b1b2b3…bm
满足以下条件之一,s<t

  • n<m 且 ai==bi
  • 存在某个k<=min(m,n),使ai==bi(i=1,2,…,k-1),ak<bk

ADT

  • 不同的高级语言对串的基本操作有不同的定义
ADT 串(string)
Data
	串中元素仅由一个字符组成,相邻元素具有前驱和后继关系
Operation
	StrAssign(T,*chars):     生成一个其值等于chars的串T
	StrCopy(T,S):            由串S复制得到串T 
	ClearString(S):          将串S清空
	StringEmpty(S):          若串为空串返回TRUE否则返回FALSE
	StringLength(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):用Sub返回串S的第pos个字符起长度为len的子串
	Index(S,T,pos):          若子串中存在和串T值相同的子串,则返回它在主串中第pos个字符之后第一次出现的位置,否则函数值为0  
	Replace(S,T,V):          用V替换串S中出现的所有与T相等的不重叠的子串
	StrInsert(S,pos,T):      在串S的第pos个字符之前插入串T
	StrDelete(S,pos,len):    从串S中删除从第pos个字符起出长度为len的子串
	DestroyString(S):        销毁串S 
endADT

顺序存储

  • 用一组地址连续的存储单元(定长的数组)来存储串中的字符序列
  • 用堆存储,由动态分配函数malloc()和free()管理存储空间

链式存储

  • 一个结点可存放多个字符,最后一个结点未被占满用#补全
  • 不如顺序存储好

朴素的模式匹配算法

  • 需要不断地回溯
/* 返回子串T在主串S中第pos个字符之后的位置,若不存在则返回0 */
/* T非空 1<=pos<=StrLength(S) */
int Index( String S, String T, int pos )
{
	int i = pos;	            // i定位主串当前位置下标
	int j = 1;		            // j定位子串当前位置下标
	
	while( i <= S[0] && j <= T[0] )	  // 长度存在S[0]、T(0)中
	{
		if( S[i] == T[i] )
		{
			i++;
			j++;
		}
		else			
		{
			i = i-j+2;		 // i退回到上次匹配首位的下一位
			j = 1;
		}
	}
	
	if( j > T[0] )             // 匹配成功,最后 j==T[0]+1
	{
		return i - T[0];       // 返回子串在主串pos之后的位置
	}
	else
	{
		return 0;
	}
}

KMP模式匹配算法

  • 不需回溯
  • 【next数组】:记录子串每个位置匹配失败时,下一次与主串该位置比对的子串下标
  • next[0]=0,next[1]=1,之后为 该位置之前序列的最长相同前后缀+1
#include <stdio.h>

typedef char *String;

/* 求next数组 */
void get_next( String T, int *next ){
	int i,j;
	i = 1;
	j = 0;
	next[1] = 0;
	while( i < T[0] ){                   // T[0]存放子串T的长度
		if( j==0 || T[i] == T[j] ){      // T[i]表示后缀的单个字符
			i++;                         // T[j]表示前缀的单个字符
			j++;
			next[i] = j;
		}
		else{
			j = next[j];                 // 若字符不同,则j值回溯
		}
	}
}

/* 返回子串T在主串S中第pos个字符之后的位置,若不存在则返回0 */
/* T非空 1<=pos<=StrLength(S) */
int Index_KMP( String S, String T, int pos ){
	int i = pos;                       // i为主串S当前位置下标
	int j = 1;                         // j为子串T当前位置下标
	int next[255];                     // 定义next数组
	get_next( T, next );               // 得到next数组
	while( i <= S[0] && j <= T[0] ){
		if( j==0 || S[i] == T[j] ){
			i++;
			j++;
		}
		else
		{
			j = next[j];               // j回退合适位置
		}
	}

	if( j > T[0] )
	{
		return i - T[0];
	}
	else
	{
		return 0;
	}
}

/* 测试 */
int main(){
    char str[255] = "ababaaaba";
    char str2[255] = "aaba";
    int next[255];
    int i = 1;
    str[0] = 9;
    str2[0] = 4;
    get_next(str, next);
    for(i=1;i<=9;i++){
        printf("%d\t",next[i]);
    }
    printf("\n%d\n",Index_KMP(str,str2,1));
    return 0;
}

在这里插入图片描述

KMP算法的改进

  • 【nextval数组】:解决子串T回溯的位置与该位置字符相同的问题
#include <stdio.h>

typedef char* String;

void get_nextval( String T, int *nextval )
{
	int j = 0;
	int i = 1;
	nextval[1] = 0;

	while( i < T[0] )
	{
		if( 0 == j || T[i] == T[j] )
		{
			i++;
			j++;                       // 于普通KMP相比仅改动如下:
			if( T[i] != T[j] )         // 若当前字符与前缀字符不同
			{
				nextval[i] = j;        // j为nextval在i位置的值
			}
			else                       // 若当前字符与前缀字符相同
			{
				nextval[i] = nextval[j];  // 将前缀字符的nextval赋给i位置的nextval
			}
		}
		else
		{
			j = nextval[j];
		}
	}
}


int Index_KMP( String S, String T, int pos )
{
	int i = pos;
	int j = 1;
	int nextval[255];

	get_nextval( T, nextval );
	
	while( i <= S[0] && j <= T[0] )
	{
		if( 0 == j || S[i] == T[j] )
		{
			i++;
			j++;
		}
		else
		{
			j = nextval[j];
		}
	}

	if( j > T[0] )
	{
		return i - T[0];
	}
	else
	{
		return 0;
	}
}

/* 测试 */
int main(){
    char str[255] = "ababaaaba";
    char str2[255] = "aaba";
    int nextval[255];
    int i = 1;
    str[0] = 9;
    str2[0] = 4;
    get_nextval(str, nextval);
    for(i=1;i<=9;i++){
        printf("%d\t",nextval[i]);
    }
    printf("\n%d\n",Index_KMP(str,str2,1));
    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值