首页 > 其他分享 >04串

04串

时间:2024-06-24 12:09:35浏览次数:3  
标签:字符 匹配 04 模式 next 算法 指针

串即字符串,由一个或多个字符组成的有限序列
串是一种特殊线性表,限制了数据类型的线性表,数据间关系成线性关系
串为了方便算法设计,第一个字符存放的下标可以从1开始

串的基本操作

void StrAssign(&T,chars); //赋值操作
void strCopy(&T,S); //复制操作
bool StrEmpty(S); //判空操作
int StrCompare(S,T); //比较操作
int StrLength(S); //计算串长
void Concat(&T,S1,S2); //串联结
int Index(S,T); //定位子串T在主串S中的位置
void ClearString(&S); //清空操作
void DestroyString(&S); //销毁操作

字符集编码

英文字符一般使用ASCII字符集,每个字符仅占1B,有效序号为0-127

其他字符集,例如Unicode字符集。每种字符集可以由多种编码方案,例如Unicode字符集可以采用UTF-8、UTF-16两种不同的方案
不同的字符集、编码方案会导致每个字符占用的空间不同,考研中默认统一每个字符占1B

暴力匹配法(朴素模式匹配算法)

朴素模式匹配算法

假定主串长度为n,匹配串长度为m
将主串中所有长度为m的子串依次与模式串对比,直到找到一个完全匹配的子串或所有子串都不匹配为止
至多匹配n-m+1个子串

算法过程

从主串S第一个字符开始,与模式串T的第一个字符比较,若相同则比较下一个字符;否则从主串的下一个字符起,重新与模式串中的字符进行比较
直到模式串中的每个字符依次在主串中匹配到,则说明匹配成功,并返回匹配成功时子串第一个字符的序号;若最后匹配失败返回0

至多运算(n-m+1)m=nm+m-m^2次,一般m<<n,所以最坏时间复杂度O(mn)

KMP算法

在朴素模式匹配算法中,当匹配模式串中间字符过程中发生匹配失败,直接回溯主串下一个字符往往会出现重复匹配,造成时间浪费

kmp算法在回溯方面进行优化改进,当模式串第一个字符匹配失败,主串指针+1,模式串指针回溯到1;当模式串中间字符匹配失败,主串指针不变,模式串指针回溯相应位置即可
为了方便回溯,可直接将不同匹配失败的回溯位置直接存放在next数组中,当发生匹配失败直接回溯模式串指针即可

//kmp算法
int Index_KMP(SString S,SString T,int next[]){
    //规定指针下标从1开始
    int i=1,j=1;
    while(i<=S.lenght && j<=T.length){
        //模式串匹配结束说明匹配成功,否则直到主串匹配结束说明匹配失败
        if(!j || S.data[i]==T.data[j])
            //顺利匹配,主串、模式串指针均后移
            //若上一次模式串第一个字符匹配失败,模式串指针后移j=0->j=1,主串指针后移
            i++,j++;
        else
            //若匹配失败,模式串指针回溯,主串指针不动
            j=next[j];
    }
    if(j>T.length)
        //模式串指针越界说明匹配成功,直接返回匹配子串首字符序号
        return i-T.length;
    else 
        //匹配失败返回0
        return 0;
}

求模式串next数组

考研暂不要求算法实现,但需要掌握手算方法,next数组长度与模式串长度m相同,并且下标从1开始

next数组的作用是当发生匹配失败时,模式串指针需要回溯到合适的位置以加快效率,同时显然任何模式串next数组的第一个位置的值都是确定的next[1]=0

同时分析,当第二个字符匹配失败时,任何模式串一定会尝试匹配第一个字符,显然next[2]=1

其余位置匹配失败需要逐步分析

假定模式串第j位置上匹配失败,那么主串[i-j+1]~[i-1]位置上的每个字符与模式串[1]~[j-1]位置上的字符是一一对应的
此时,模式串指针j依次-1,重新匹配主串[i-j+1]~[i-1]位置上的字符是否和模式串[1]~[j-1]位置上的字符是否对应,如果对应,则将j的值填入next数组中;否则,直接把1填入next数组

显然可以确定,除了next[0]、next[1]的值是确定的,其余位置的值都是与模式串本身相关

next数组手算方法算法实现

//字符串比较
bool strcomp(SString S1,SString S2,int lenth){
    int i;
    for(i=1;i<lenth;i++)
        //如果匹配失败,直接跳出循环
        if(S1.data[i]!=S2.data[i]) break;
    //若匹配成功,i的值为子串长度,反之说明匹配失败
    return i==lenth;
}

//求next数组算法
void Next_KMP(SString T,int next[]){
    //规定指针下标从1开始
    int i=1;
    //next[1]=0,并且循环从next[2]开始
    next[i++]=0;
    while(i<=T.length){
        int j;
        for(j=1;j<i;j++)
            if(strcomp(T,T+j,i-j)) break;
        next[i]=i-j;
        i++;
    }
}

上述手算方法,由于考题的规模比较小,人眼看子串是否匹配更加高效,但是对于计算机而言时间复杂度为反而更高了(执行次数为$12+2+...m^2$)

对于计算机去求解next数组,更加适合以下实现方式

假设next[j]=k,此时next[j+1]有两种情况

  • 新增的j字符和k字符相同,即继续重复出现前缀如aaaaaa,显然有next[j+1]=next[j]+1=k+1
  • 新增字符和之前的字符均不相同,如aaaaab,那么由于不存在更短的相同前缀,可以直接确定next[j+1]=next[j]
//求next数组算法
void Next_KMP(SString T,int next[]){
    //规定i指针下标从1开始
    //指针j用于统计之前连续相同字符的数量,j=0将next[1]情况跳过
    int i=1,j=0;
    //初始化next[1]=0
    next[1]=0;
    while(i<T.length){
        //由于next数组i先+1后存值,因此循环条件为length-1
        if(!j || T.data[i]==T.data[j]){
            //j==0确保next[1]=1的值填入
            //j==1后,j=i-1,每次只要比较前后字符是否相同,相同则j++,实现next[i+1]=next[i]+1,同时j为当前连续相同前缀字符数量
            i++,j++;
            next[i]=j;
        }
        else
            //当前后字符不同时,连续前缀数量并不发生改变,直接继承之前的数量,next[i+1]=j
            j=next[j];
    }
}

总结起来,KMP算法最坏时间复杂度为O(m+n),其中求next数组时间复杂度为O(m),模式匹配最坏时间复杂度为O(n)

朴素匹配算法最坏情况比kmp算法要慢,但是一般情况下朴素匹配算法时间复杂度也为O(m+n),只有出现主串和子串有很多部分匹配的情况下效率更高

KMP改进算法

在next数组中,如果在j位置发生匹配失败后,指针j会跳到next[j]位置,但是如果T[j]==T[next[j]],即跳转后的字符和之前一样的情况,那么依旧会发生匹配失败,指针j可以再跳到next[next[j]],从而达到进一步优化

这一步改进优化,实质上时对next数组的值进行优化,匹配算法不需要修改,即如果模式串中j位置的字符与next[j]位置的字符相同,可以直接赋值next[j]=next[next[j]]

//求nextval数组算法
void Next_KMP(SString T,int nextval[]){
    //规定i指针下标从1开始
    //指针j用于统计之前连续相同字符的数量,j=0将next[1]情况跳过
    int i=1,j=0;
    //初始化next[1]=0
    next[1]=0;
    while(i<T.length){
        //由于next数组i先+1后存值,因此循环条件为length-1
        if(!j || T.data[i]==T.data[j]){
            //j==0确保next[1]=1的值填入
            //j==1后,j=i-1,每次只要比较前后字符是否相同,相同则j++,实现next[i+1]=next[i]+1,同时j为当前连续相同前缀字符数量
            i++,j++;
            if(T.data[i]==T.data[j])
                //当j位置的字符和当前字符相同时,由于仍旧会匹配失败,直接访问nextval
                nextval[i]=nextval[j];
            else
                //相反,和之前的方式相同
                nextval[i]=j;
        }
        else
            //当前后字符不同时,连续前缀数量并不发生改变,直接继承之前的数量,next[i+1]=j
            j=nextval[j];
    }
}

以上确实对kmp算法进行了优化,但是时间复杂度上分析依旧是O(m+n),主要是对模式串中子串部分匹配问题进行了优化,一般情况下效率相差不多

标签:字符,匹配,04,模式,next,算法,指针
From: https://www.cnblogs.com/GKJerry/p/18264782

相关文章

  • nginx配置负载均衡,nginx负载均衡404错误
    nginx在nginx.conf配置文件中通过upstream模块和server模块的配合使用,就可以实现负载均衡。在http的upstream模块中,可以通过server指令指定后端服务器的IP地址和端口,同时还可以设定每个后端服务器在负载均衡调度中的状态。常用的状态有:weight:服务访问的权重,默认是1。down:表示当......
  • Klipper RP2040 display ssd1306 0.96 屏幕配置
    接线屏幕接线parampinGNDGNDVCCVCCSCLSDA编码器接线parampinGNDGNDEN1VCCEN2CLklipper配置#显示屏及旋钮[display]lcd_type:ssd1306#i2c_bus:i2c0dencoder_pins:^gpio24,^gpio23encoder_steps_per_detent:2c......
  • 04_搭建一个VUE3前端架子+gitee配置
    1.创建一个文件夹HCJV_012.vscode打开该文件夹,打开终端。3.使用vite安装,选择vue,选择JavaScript,项目名称demo01cnpmcreatevite@latest4.跳转demo01目录下cddemo015.安装cnpmcnpminstall尝试执行下:npmrundev6.安装VueRoutercnpminstallvue-router@47.......
  • 在Linux中,如何将本地 80 端口的请求转发到 8080 端口?当前主机 IP 为10.0.0.104。
    在Linux系统中,将本地80端口的请求转发到8080端口,可以通过使用iptables命令来实现。当前主机IP为10.0.0.104,具体命令如下:iptables-tnat-APREROUTING-d10.0.0.104-ptcp--dport80-jDNAT--to-destination10.0.0.104:8080解析:iptables:iptables命令用于配置Linux内核......
  • Psim仿真教程04-仿真软件功能介绍/电源工程师初级到高级进阶之路
    目录点击下面文字下载需要的版本:Psim2022中文版下载链接:Psim2022中文版软件下载地址Psim9.1经典版本下载链接:Psim9.1软件下载地址1.Psim软件的主要界面1.1文件菜单栏:1.2编辑菜单栏:1.3视图菜单栏1.4视图选项中的元件清单1.5视图选项中的元件数目菜单可以统计仿......
  • [题解]P2042 [NOI2005] 维护数列 - Splay解法
    P2042[NOI2005]维护数列一道思路不难,但实现细节很多的平衡树题,调了一天半终于做出来了w。对于初始序列,我们可以直接构建一条链(毕竟一个一个调用插入函数也可能形成一条链)。题解有递归直接构建成一棵严格平衡的二叉树的,这样也可以,常数可能会小一点。其中区间反转就是裸的文艺......
  • AI 大模型应用开发实战(04)-AI生态产业拆解
    1行业全景图2结构拆解AIGC生成式AI这个产业。分成上中下游三大块。2.1上游基础层主要包括:算力:包括AI芯片和云服务等,例如像英伟达、AMD以及华为等厂商提供的算力基础设施。大型模型基于Transformer架构,对算力的需求很大。数据:新时代的石油,分为基础数据服务、数据集和向......
  • C++题解(1) 信息学奥赛一本通 1003:对齐输出 洛谷 B2004:对齐输出 土豆编程 T1003:对
    【题目描述】读入三个整数,按每个整数占8个字符的宽度,右对齐输出它们,按照格式要求依次输出三个整数,之间以一个空格分开。【输入】只有一行,包含三个整数,整数之间以一个空格分开。【输出】只有一行,按照格式要求依次输出三个整数,之间以一个空格分开。【输入样例】......
  • P2404 自然数的拆分问题
    #include<bits/stdc++.h>#include<math.h>#include<cmath>usingnamespacestd;intmain(){   intn;   cin>>n;   if(n==2)cout<<"1+1";   elseif(n==3){      cout<<"1+1+1"<<endl;     ......
  • s2-045漏洞还原
    1.环境准备,本地server2003环境对应网址http://192.168.116.112:8080/struts2-showcase/showcase.action2.使用在线工具包扫描漏洞信息输入对应的url检测发现存在对应漏洞选择对应漏洞,可执行对应系统命令 上传自带木马文件(复制tmp.jsp内容,上报到对应目录下C:\jspstu......