目录
4.1串类型的定义
![](https://img-blog.csdnimg.cn/b774ee7b60be4c45b5efb633872f8cd6.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bmz5YWJ6ZWc5ZCO55qE5bCP5ZGG5q-b,size_20,color_FFFFFF,t_70,g_se,x_16)
子串:串中任意个连续的字符组成的子序列。
主串:包含子串的串。
子串的位置:子串的第一个字符在主串中的序号。
4.1.1串类型的定义
插入:
删除:
串赋值:
串赋值:
串链接:
串置换:
假设 S = ²abcaabcaaabca², T = ²bca ²
若 V = ²x ², 则经置换后得到 S = ²axaxaax ²
求子串:
定位函数:
基本操作:
上述 的所有操作中,串赋值StrAssign,串比较StrCompare,求串长StrLength,串链接Concat,
以及求最小子串SubString
思考:StrDelete (&S, pos, len) (删除)怎么用上面的操作实现?
设目标串的长度为length:
则有:
void StrDelete(&S,pos,len){
length=Strlength(S);
Concat(&T,SubString(&S,1,pos-1),SubString(&S,pos+len,length-(pos+len-1)));
StrAssign(&S,&T);
}
串和对应线性表的比较:
串的逻辑结构约束和线性表即为相似,区别仅仅在于串的数据对象约束为字符集
在线性表的基本操作中,大多以“单个元素”作为操作对象;
而在串的基本操作中,通常以“串的整体”作为操作对象。
答:Strlength(s) = 14;
SubString(s,8,7)=STUDENT;
SubString(t,2,1)="O";
Index(s,'A')=3;
Index(s,t,3)=0;(s中没有t);
Replace(s,'STUDENT',q)=’I AM A WORKER’;
4.2串类型的表示和实现
4.2.1静态存储分配:
#define Maxstrlen 255 //用户可用的最大串长
typedef unsigned char SString[ Maxstrlen+1 ] ;
SString s; //s是一个可容纳255个字符的顺序串。
一般用 SString[0] 存放串长信息;
关于串的操作中,串链接操作属于静态存储分配
4.2.2堆存储分配
讨论:想存放超长字符串怎么办?——静态数组有缺陷!
改用动态分配的一维数组——“堆”!
特点:仍用一组连续的存储单元来存放串,但存储空间是在程序执行过程中动态分配而得。
特点:用malloc函数,也可用relloc函数
关于串的操作中,插入操作,赋值操作属于堆分配存储操作。
4.2.3链式存储
特点:用链表存储串值,易于插入和删除。
法1的存储密度为:1\2 法二存储密度为:9\15=3\5
显然若数据元素更多时,法二(块链结构)更优。
块链的定义:
#define CHUNKSIZE 80 //可由用户定义的块大小
typedef struct Chunk { //首先定义结点类型
char ch [ CHUNKSIZE ]; //结点中的数据域
struct Chunk * next ; //结点中的指针域
}Chunk;
typedef struct { //其次定义用链式存储的串类型
Chunk *head; //头指针
Chunk *tail; //尾指针
int curLen; //结点个数
} Lstring;
的链式存储结构对某些操作比较方便,但是总体来说没有前面的两种存储结构灵活。
4.3串的匹配模式算法
模式匹配:即子串定位运算 index函数
算法目的:确定主串中所含子串第一次出现的位置
初始条件:1.串s(主串)2. 和 串T(模式串)存在 T是非空串 3. 1≤pos≤StrLength(s)
操作结果:若主串S中存在和串T值相同的子串,则返回它在主串S中第pos个字符之后第一次出现的位置;否则函数值为0。
4.3.1BF算法
① BF算法设计思想:
若相等,继续逐个比较后续字符;
若不等,从主串的下一字符(pos+1)起,重新与第一个字符比较。
•直到主串的一个连续子串字符序列与模式相等 。返回值为S中与T匹配的子序列第一个字符的序 号,即匹配成功。
否则,匹配失败,返回值 0 .
BF算法的代码:
Int Index (SString S, SString T, int pos) {
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
S[0],T[0] 是储存字符串长度信息的。
思考:
(1)i<=S[0]可以改成S[0]-T[0]+1吗?
(2)循环执行的次数最多是多少?
若n为主串长度,m为子串长度,则串的BF匹配算法最坏的情况下需要比较字符的总次数为O(n*m)
4.3.2KMP算法
① KMP算法设计思想
能否利用模式串和主串部分匹配的结果加速模式串的滑动速度?要滑动到哪里?
能并且主串S的指针不必回溯,时间复杂度可以从O(n*m)提速到O(n+m)
② KMP算法的推导过程
抓住部分匹配结果的两个特征:
设目前应与T的第k字符开始比较
则T的k-1~1位=S前i-1~i-(k-1)位
即:S中位置i前面的k-1位字符串=T中的前k-1位
刚才是在S的i处和T的第j字符 处失配
则S前i-1~i-(k-1)位=T的j-1~j-(k-1)位
即:S中位置i前面的k-1位字符串=T中j前面的k-1位
两式联立可得:‘T1…Tk-1’=‘Tj-(k-1) …Tj-1’
意为:这一次新配对位K的前K-1位和上一次失配处的前k-1位是相同的。
注意:j为已知此处的失配位置,我们的目标是计算新的起点k(即该失配处j下一次应该和)。
只剩一个未知数k,理论上已可解,且k仅仅和模式串k有关。
所以根据模式串的规律:
‘T1…Tk-1’=‘Tj-(k-1) …Tj-1’
和已知的当前失配位置j ,可以归纳出计算新起点 k的表达式。
令k = next[ j ],则
为什么j=1时 next[j]=0 ?
j=1,意思就是第一个字符就匹配失败,下一趟的比较应该让i++,j变成1,为了下一趟是i++,j++同时增加,需要在上一趟时把j=0,然后j++后就会变成1,这时候i也增加了1.
怎样计算模式T所有可能的失配点 j 所对应的 next[j]?
j=1时, next[ j ]≡ 0;因为属于“j=1”;
j=2时, next[ j ]≡ 1;因为属于“其他情况”;
j=3时, k={2},只需查看‘T1’ ≠ ‘T2’;
j=4时, k={2,3},要查看‘T1’=‘T3’ 和‘T1T2’‘≠ T2 T3’
j=5时, k={2,3,4},要查看‘T1’=‘T4’ 、‘T1T2’ ≠‘T3T4’ 和
‘T1T2T3’‘≠ T2T3T4’
所谓的max值的是k可以取得集合中满足匹配条件的k值的极大值。
求解Next的方法:
根据定义的公式的方法:用前缀和后缀的方法,非常简单。
(1) next[1]=0,求next[2]=1;
(2)求后面的next[j]时,把第1位到第j-1位的字符串取出来,求该串的前缀和后缀,前缀后缀最长是j-2,取出最长的相等的前缀和后缀,这个长度加一就是next[j]。
例如:a b a a b c a c
next[4]=? 前面的串是:aba,前缀和后缀a=a,长度是1,所以next[4]=1+1=2 (最大长度加1)
next[6]=? 前面的串是:abaab,前缀和后缀ab=ab,长度是2,所以next[6]=2+1=3(最大长度加1)
实用方法。
nextval序列的写法: