《数据结构》学习笔记-第十一章 串

1.基础知识

1.1 术语

在这里插入图片描述

1.2 ADT

在这里插入图片描述

2.串匹配(一系列字符构成的串n和子串m)

2.1 概念

文本串T,模式串P
在这里插入图片描述

四个层次问题:
在这里插入图片描述
算法评测:
在这里插入图片描述

2.2 算法

2.2.1 BF蛮力算法(O(n*m))

在这里插入图片描述
(1)i 和 j 分别指向T[ ]和P[ ]中待比对的字符
在这里插入图片描述

#include <iostream>
#include <cstring>
using namespace std;
int match(char *P,char *T){
	size_t n=strlen(T),i=0;//[Error] 'strlen' was not declared in this scope
	size_t m=strlen(P),j=0;//只需要 #include <cstring>
	while(j<m&&i<n)
		if(T[i]==P[j]){
			i++;j++;
		}
		else{
			i-=j-1;j=0;// T回退,P复位 
		} 
	return i-j;//返回匹配位置 
}
int main(){
	char *P="ll";
	char *T="Hello";
	cout<<match(P,T);
	return 0;
}

(2)i+j 和 j 分别指向T[ ]和P[ ]中待比对的字符
在这里插入图片描述
在这里插入图片描述

int match2(char *P,char *T){
	size_t n=strlen(T),i=0;//T[i]与P[0]对齐 
	size_t m=strlen(P),j;//T[i+j]与P[j]对齐 
	for(i=0;i<n-m+1;i++){//T从第i个字符起,与 
		for(j=0;j<m;j++){//P中对应的字符逐个比对 
			if(T[i+j]!=P[j]) break;//若失配,p整体右移一个字符重新比对 
		}
		if(m<=j) break;//找到匹配子串 
	}
		
	return i;//返回匹配位置 
}

2.2.2 Knuth-Morris-Pratt :KMP算法(O(n))——记忆力与预知力

(1)算法——O(n+m)

int match_lookupTable(char *P,char *T){
	int *next=buildNext(P);//构建查询表 ,O(m)
	int n=(int)strlen(T),i=0;//
	int m=(int)strlen(P),j=0;//
	while(j<m&&i<n)//O(2n-1)
		if(0>j||T[i]==P[j]){//j<0即对应比对在首字符失败
			i++;j++;
		}
		else{
			j=next[j];// P右移,T不回退, 
		} 
	delete [] next; 
	return i-j;//返回匹配位置 
}

(2)查询表(next[ ]表)
1.含义及原理(经验)
在这里插入图片描述
2.构造(next[j]<j)
在这里插入图片描述

int *buildNext(char *P){
	size_t m=strlen(P),j=0;
	int *N=new int[m]; //next表 
	int t=N[0]=-1;//哨兵 
	while(j<m-1)
		if(0>t||P[j]==P[t])
			N[++j]=++t;
		else
			t=N[t];
	return N;
}

3.改进(经验+教训)

int *buildNext(char *P){//改进 
	size_t m=strlen(P),j=0;
	int *N=new int[m]; //next表 
	int t=N[0]=-1;//哨兵 
	while(j<m-1)
		if(0>t||P[j]==P[t]){ 
			j++;t++;
			N[j]=P[j]!=P[t]?t:N[t];//若替代字符与被替代字符相同,则用N[t] 给N[j]赋值 
		}else
			t=N[t];
	return N;
}

4.与蛮力算法的对比

在这里插入图片描述

2.2.3 Boyer-Moore:BM算法——适用于字母表规模大的,如ASCLL,Unicode

结合了bc和gs表,最终的位移量选取其中的较大者

(1)BM_BC坏字符策略(最好O(n/m),最坏O(n*m))——重视教训

1.从后向前,自右向左的比对,匹配失败的情况出现的越早,排除的对齐位置越多(重视教训,因为失败的概率大于成功的概率)
在这里插入图片描述
概述:
在这里插入图片描述
2.构造bc[ ]表

int *buildBC(char *P){
	int *bc=new int[256];//bc[]表,与字母表等长 
	for(size_t j=0;j<256;j++) bc[j]=-1;//初始化,统一指向通配符 
	for(size_t m=strlen(P),j=0;j<m;j++)
		bc[P[j]]=j;//painter,保存的是最后出现的字母 
	return bc; 
}//第二个循环,引入m来避免反复调用strlen 

(2)BM_GS好后缀策略(经验:匹配的后缀)

1.概述
在这里插入图片描述
2.构造gs[ ]表(MS[ ] ->ss[ ]->gs[ ])

[1] MS[ j ]:P[ 0, j ]的所有后缀中,与P的某一后缀匹配最长者

[2] ss[ j ]=| MS[ j ] | = max { 0 <= s <= j+1 | P( j-s , j ] = P[ m - s , m ) }

int* buildSS ( char* P ) { //构造最大匹配后缀长度表:O(m)
   int m = strlen ( P ); int* ss = new int[m]; //Suffix Size表
   ss[m - 1]  =  m; //对最后一个字符而言,与之匹配的最长后缀就是整个P串
// 以下,从倒数第二个字符起自右向左扫描P,依次计算出ss[]其余各项
   for ( int lo = m - 1, hi = m - 1, j = lo - 1; j >= 0; j -- )
      if ( ( lo < j ) && ( ss[m - hi + j - 1] < j - lo ) ) //情况一:MS[ j ]足够长,以至于其就是整个模式串的前缀
         ss[j] =  ss[m - hi + j - 1]; //直接利用此前已计算出的ss[]
      else { //情况二
         hi = j; lo = __min ( lo, hi );
         while ( ( 0 <= lo ) && ( P[lo] == P[m - hi + lo - 1] ) ) //二重循环?
            lo--; //逐个对比处于(lo, hi]前端的字符
         ss[j] = hi - lo;
      }
   return ss;
}

[3] gs[ j ]

在这里插入图片描述

int* buildGS ( char* P ) { //构造好后缀位移量表:O(m)
   int* ss = buildSS ( P ); //Suffix Size table
   size_t m = strlen ( P ); int* gs = new int[m]; //Good Suffix shift table
   for ( size_t j = 0; j < m; j ++ ) gs[j] = m; //初始化
   for ( size_t i = 0, j = m - 1; j < UINT_MAX; j -- ) //逆向逐一扫描各字符P[j]
      if ( j + 1 == ss[j] ) //若P[0, j] = P[m - j - 1, m),则
         while ( i < m - j - 1 ) //对于P[m - j - 1]左侧的每个字符P[i]而言(二重循环?)
            gs[i++] = m - j - 1; //m - j - 1都是gs[i]的一种选择
   for ( size_t j = 0; j < m - 1; j ++ ) //画家算法:正向扫描P[]各字符,gs[j]不断递减,直至最小
      gs[m - ss[j] - 1] = m - j - 1; //m - j - 1必是其gs[m - ss[j] - 1]值的一种选择
   delete [] ss; return gs;
}

2.3.4 总览

在这里插入图片描述

3.另类的串匹配:Karp-Rabin算法:散列(串即是数)

3.1 转化为数

在这里插入图片描述

3.2 散列

将每个串所对应的自然数称为其指纹,如果字符集规模很大或模式串P较长,则指纹将很大,导致数位溢出在这里插入图片描述
此时就可以使用散列
(1)散列压缩
在这里插入图片描述
(2)散列冲突

在这里插入图片描述

遗留问题:快速指纹计算

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值