定义
由零个或多个字符组成的有限序列,属于每个结点都是由单字符组成的特殊的线性表。
记为:S='a1 a2 a3...an',每个ai就是一个单字符,引号内就称为串值,S就称为串名。
串的长度:n是串汇总字符的个数
空串:n=0时的串
子串:串中任意个连续的字符组成的子串称为该串的子串
主串:包含子串的串相应的称为主串
子串在主串中的位置:通常将字符在串中的序号称为字符在串中的位置。子串在主串中的位置则以子串的第一个字符在主串中的位置来表示
串相等:长度相等,对应位置字符相等
定长顺序串
以数组形式存储并设定长度变量
#define MAXLEN 20
typedef struct{
char ch[MAXLEN];
int len;
}SString;
插入函数
将被插入的串从插入位置pos分为两部分(A串和B串,长度分别为LA,LB),要插入部分为C串,长度LC,则串由插入前的AB变化为ACB,三种情况:
- 插入后串长(LA+LC+LB)≤MAXLEN:将B后移LC个位置,再插入C
- 插入后串长>MAXLEN且pos+LC < MAXLEN:B后移LC个位置,B一部分被舍弃
- 插入后串长>MAXLEN且pos+LC > MAXLEN:B全部被舍弃(不用后移),C插入后一部分被舍弃
在串s中序号为pos的字符之前插入串t
StrInsert(SString *s,int pos,SString t)
{
int i;
if(pos<0||pos>S->len) //若插入位置不合法
return 0;
if(s->len + t.len<=MAXLEN) //插入后串长放得下
{
for(i=s->len+t.len-1;i>=pos+t->len;i--) //从串尾移到空出来的最后位置
s->ch[i]=s->ch[i-t.len]; //后移一位
for(i=0;i<t.len;i++)
s->ch[i+pos]=t.ch[i]; //依次在s中放入t
s->len=s->len+t.len; //修改长度
}
else if(pos+t.len<=MAXLEN) //插入后t可以全部放入
{
for(i=MAXLEN-1;i>pos+t.len-1;i--) //已经有溢出的话,直接从尾巴开始
s->ch[i]=s->ch[i-t.len];
for(i=0;i<t/len;i++)
s->ch[i+pos]=t.ch[i];
s->len=MAXLEN; //串长已满
}
else //串t都不能完全放下
{
for(i=0;i<MAXLEN-pos;i++)
s->ch[i+pos]=t.ch[i]; //也不用挪了,直接放
s->len=MAXLEN;
}
}
朴素串匹配
求从位置pos算起,子串(模式串 )t在主串S 中的位置
StrIndex(SString s,int pos,SString t)
{
int i,j; //分别指向主串和子串
if(t.len==0)
return 0; //若t根本没有长度
else
{ i=pos; //i扫描主串
j=0; //j扫描子串
while(i<s.len&&j<t.len) //都还在范围内
{
if(s.ch[i]==t.ch[j])
{i++;j++;} //后移
else
{i=i-j+1;j=0} //主串从上次对比的下一位开始
}
if(j>=t.len) //退出循环就说明有一个不满足,即t已经挨个扫描完了
return (i-j) //当前i保存的位置减去j位置(也就是长度)
else //当s都扫描完了,t还没扫描完
return 0;
}
}
这是带回溯的算法,时间复杂度较高
连接操作
连接后串长小于极限的话,直接连接;若大于则舍弃后面串内字符
删除操作
首先判断是否合法,然后使用被删除位置后面的字符覆盖前面字符,缩减串长即可舍弃后面的字符
复制串
对应赋值即可
判空
若串->len为0,则判断为空
串比较
一个一个对象比较即可
求串长
直接就是s->len
清空
s->len=0;
堆串
将一个地址连续,容量很大的存储空间作为字符串的可用空间
像数组一样,串与串之间首尾相连,每个串指针包含内容和长度;例如A指针指向A串并告知串长,B指针就能确定在A串长后面的位置。
数据类型
注意:这里只有指针,没有存储具体内容
typedef struct{
char *ch;
int len;
}HString;
len域指示串长度,ch域指示串起始地址
定位函数
与顺序串没有本质差别
StrIndex(HString s,int pos,HString t)
{
int i,j;
if(s.len==0||t.len)
return 0;
i=pos;
j=0;
while(i<s.len && j<t.len)
{
if(s.ch[i]==t.ch[i])
{
i++;
j++;
}
else
{
i=i-j+1;
j=0;
}
}
if(j>=t.len)
return (i-j);
else
return 0
}
块链串
不同于以前每个结点中放一个字符,现在每个结点内放一个字符或多个字符(字符串 ),这样的结点称为“块”,整个链表叫“块链表”,为了便于操作,增加一个尾指针。
不便之处在于:在一个结点的字符串中插入
类型结构
#define BLOCKSIZE 4
typedef struct Block{
char ch[BLOCKSIZE]; //这个结点能存4个字符
struct Block *next;
}Block; //结点类型
typedef struct{
Block *head; //Block类型的头指针
Block *tail; //Block类型的尾指针
int len;
}BLString; //链表类型
结点大小
指数据所占大小,不包含指针域,例如上面定义的结点,结点大小就是4。
存储密度
结点大小/定义大小。定义大小就是带上指针域的整个结点大小。顺序结构密度是1
例如上面定义结点的指针域如果是1,存储密度:4/5=0.8。
应用举例:链表的简单匹配
带头结点的单链表实现串的模式匹配,每个结点放一个字符,定义链串类型LKString。
Link *StrIndex(LKString *s,LKString *t) //主串s,子串t
{ Link *sp,*tp,*start;
if(t->len==0)
return s->head->next; //空串是任意串的子串
start=s->head->next; //定位到主串的首元
sp=start; //sp开始从start扫描
tp=t->head->next; //子串t开始扫描
while(sp!=NULL&&tp!=NULL)
{
if(sp->ch==tp->ch)
{
sp=sp->next;
tp=tp->next;
}
else
{
start=start->next; //没匹配上start后移一位
sp=start; //从start扫描
tp=t->head->next;
}
}
if(tp==NULL)
return start; //tp指到空,都查完了,返回主串的起始位置
else
return NULL;
}
应用举例:行编辑器
利用行列表找到相应的位置
具体思路:www.bilibili.com/video/BV1kx…
KMP算法(看教材)
每一次匹配过程中出现的字符比较不等时,不回溯指针,利用已经得到的“部分匹配”结果,将模式向右滑动尽量远的距离
时间复杂度不是原来的O(m*n),变成了O(m+n)
算法主体
根据子串中相同的前后缀:**当出现子串匹配失败时,子串后缀与主串有相同部分,直接将子串前缀对应到失败位置之前的相同部分,**减少子串需要扫描的部分,该方法主串也不回溯。
int StrIndex(SString s,SString t,int pos)
{
i=pos;
j=1;
while(i<s.len&&j<t.len)
{
if(s.ch[i]&&t.ch[j])
{
i++;
j++;
}
else
j=next[j];
}
if(j>t.len)
return i-t.len;
else
return 0;
}
获取next[j] (next[1]=0)
根据next[j]控制j回溯到哪个位置,k值仅和模式串(子串)有关,和主串无关。
其实是找前后缀相等时,k=最大相同长度+1
当j=1时,k=0;若有无相同前后缀,则k=1;若有n项相同,k=n+1。
next数组本质是保存子串某个位置前相同前后缀个数
需要注意,next数组没有next[0]
void get_next(SString t,int &next[]){
int j=1,k=0;
next[1]=0;
while(j<t.len)
{
if(k==0||t.ch[j]==t.ch[k]) //当追溯到头也没有相同,或找到相同
{
++j;
++k;
next[j]=k;
}
else
k=next[k]; //向前追溯
}
}
先说明j是next数组的下标,k是前后缀相同个数+1
主体思想:模式串next[1]设定为0,当j没有数完子串所有字符的时进行循环。如果k=0时候(也就是最后都找到next[1]了)或者j做下标的字符=k做下标的字符:j+1(操作位置变成下一个),k=k+1(说明前后缀中又做了一位相同),给操作位置赋值现在的k。否则,说明两个下标所指字符不相等,将下标k所指next[k]中所存的下标赋值给k,让k在循环中再次进入上面的判断,直到找到或者找到头都没有。
建议看着代码手写几个感受一下
一个up的详细讲解:www.bilibili.com/video/BV1i6…