算法与数据结构【算法】——四、字符串匹配:基础字符串、朴素匹配与KMP

算法与数据结构【算法】——四、字符串匹配:基础字符串、朴素匹配与KMP

一、串的抽象数据类型定义

(1)数据对象:

D={ ai |ai∈CharacterSet, i=1,2,…,n, n≥0 }

(2)数据关系:

R1={ < ai-1, ai > | ai-1, ai ∈D,i=2,…,n }

(3)基本操作:
操作操作操作
StrAssign (&T, chars) 创建并赋值StrCopy (&T, S) 串复制DestroyString (&S) 串销毁
StrEmpty (S) 判断空串StrCompare (S, T) 串比较StrLength (S) 求串长
Concat (&T, S1, S2) 串连接SubString (&Sub, S, pos, len) 串截取,pos起len个,返回到SubStrDelete (&S, pos, len) 串删除,pos起len个
Replace (&S, T, V) 串替换(不重叠)StrInsert (&S, pos, T) 串插入,pos位之前Index (S, T, pos) 在pos位后查找,返回在主串位置
ClearString (&S) 串清空
  • 最小操作子集
    • 串赋值StrAssign、串复制Strcopy、 串比较StrCompare、求串长StrLength、 串联接Concat、求子串SubString
    • 以上操作能构成其他基本操作。( 除串清除ClearString和串销毁DestroyString )

二、串与线性表的区别

  • 串的数据对象约束为字符集
  • 串的基本操作和线性表有很大差别。
    • 在线性表的基本操作中,大多以“单个元素”作为操作对象;
    • 在串的基本操作中,通常以“串的整体”作为操作对象。

三、串的表示和实现

(1)串的定长顺序存储表示
	#define  MAXSTRLEN  255   // 在255以内定义最大串长
	typedef unsigned char  Sstring[MAXSTRLEN + 1];  // 0号单元存放串的长度
  • 串的实际长度可在这个定义长度的范围内随意设定
  • 超过定义长度的串值则被舍去,称之为“截断”
    • 合并字符串:a. 情况一:未截断,S1+S2 b. 情况二:S1+部分S2 c. 情况三:部分S1
  • 基本操作: “字符序列的复制”
(2)串的堆分配存储表示
typedef struct {
	char *ch;       // 若是非空串,则按串长分配存储区,否则ch为NULL
	int  length;   // 串长度
} HString;
  • C的串类型属于该法存储,以一个空字符为结束符,串长是一个隐含值。
  • 系统利用函数malloc( )和free( )进行串值空间的动态管理,为每一个新产生的串分配一个存储区,即"堆",称串值共享的存储空间
  • 实现的算法为:先为新生成的串分配一个存储空间,然后进行串值的复制。
    • 合并字符串:释放旧空间,申请新空间,直接复制
    • 求子串:
      Sub.ch = (char )malloc(lensizeof(char));
      Sub.ch[0…len-1] = S[pos-1…pos+len-2];
      Sub.length = len;
(3)串的块链存储表示
  • 可用链表来存储串值,通常一个结点中存放的不是一个字符,而是一个子串
    • 原因:(由于串的数据元素是一个字符,它只有 8 位二进制数)
  • 代码实现:
	#define CHUNKSIZE 80  // 可由用户定义的块大小
	typedef struct Chunk{  // 结点结构
		char ch[CUNKSIZE]struct Chunk *next;
	}Chunk;
	typedef struct{    // 串的链表结构
		Chunk *head, *tail;     // 串的头和尾指针
		int curlen;      //串的当前长度
	}Lstring;

四、串的模式匹配算法——单模式串匹配

串匹配函数:INDEX (S, T, pos) ,S 主串,T 模式串
单模式串匹配:一个串跟一个串进行匹配 —— BF 算法 和 RK 算法 —— BM 算法 和 KMP 算法
多模式串匹配算法:一个串中同时查找多个串 —— Tire 树 和 AC自动机

(1)简单算法
  • BF算法, Brute Force 的缩写,中文叫作暴力匹配算法,也叫朴素匹配算法
    • 在主串S中,检查起始位置分别是 0、1…n-m 且长度为 m 的 n-m+1 个子串,看有没有跟模式串匹配的
    • 时间复杂度:O(n*m)
    • 常用算法——KISS(Keep it Simple and Stupid)设计原则
int Index(SString S, SString T, int pos) { // 返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数值为0。
      i = pos;   j = 1;
      while (i <= 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;
} // Index
  • RK算法——优化
    • Step1:通过哈希算法对主串中的 n-m+1 个子串分别求哈希值
    • Step2:逐个与模式串的哈希值比较大小,若相等则匹配
    • 将字符串匹配改为数组匹配,效率提升,但是算法整体的效率并没有提高
      • 考虑26进制哈希,则相邻哈希值有相关性,则提升算法效率——O(n)
      • 哈希冲突?
(2)首尾匹配算法
  • 先比较模式串的第一个字符,再比较模式串的最后一个字符,最后比较模式串中从第二个到第n-1个字符。
(3)KMP算法 O(m+n)
  • BM 算法 到 KMP算法 (此处之后再细看)
  • 原理
    • 当 S[i] <> T[j] 时,已经得到的结果:S[i-j+1…i-1] == T[1…j-1] (前面相等,新的匹配不等)
    • 若已知 T[1…k-1] == T[j-k+1…j-1] (已知匹配传的字串有重复, 回缩),则有 S[i-k+1…i-1] == T[1…k-1]
  • 1、串匹配
	int IndexKMP(SStringS, Sstring T, int pos){
		i=pos; j=1;
		if (j=0 || S[i]==T[i]) {++i;  ++j;}   //继续比较后继字符
		else j=next[j];  //模式串向右移动
		if (j>T[0]) return i-T[0];  /*匹配成功*/  else return 0;
	}
  • 2、自匹配——求next
    在这里插入图片描述
  • 主串和模式串是同一个串 的 自匹配
	Void get_next(String &T,int &next[]{   //求模式串T的next函数值并存入数组next
		i=1;  next[1]=0;j=0while (i<T[0])
			if (j==0 || T[i]==T[j])  {  ++i;  ++j;  next[i]=j; }    else  j=next[j];
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值