【模板】KMP字符串匹配

2023-05-16

题目描述

给出两个字符串 s_1s1​ 和 s_2s2​,若 s_1s1​ 的区间 [l, r][l,r] 子串与 s_2s2​ 完全相同,则称 s_2s2​ 在 s_1s1​ 中出现了,其出现位置为 ll。
现在请你求出 s_2s2​ 在 s_1s1​ 中所有出现的位置。

定义一个字符串 ss 的 border 为 ss 的一个非 ss 本身的子串 tt,满足 tt 既是 ss 的前缀,又是 ss 的后缀。
对于 s_2s2​,你还需要求出对于其每个前缀 s's′ 的最长 border t't′ 的长度。

输入格式

第一行为一个字符串,即为 s_1s1​。
第二行为一个字符串,即为 s_2s2​。

输出格式

首先输出若干行,每行一个整数,按从小到大的顺序输出 s_2s2​ 在 s_1s1​ 中出现的位置。
最后一行输出 |s_2|∣s2​∣ 个整数,第 ii 个整数表示 s_2s2​ 的长度为 ii 的前缀的最长 border 长度。

输入输出样例

输入 #1复制


ABABABC
ABA
  

输出 #1复制


1
3
0 0 1 
  

说明/提示

样例 1 解释

对于 s_2s2​ 长度为 33 的前缀 ABA,字符串 A 既是其后缀也是其前缀,且是最长的,因此最长 border 长度为 11。

数据规模与约定

本题采用多测试点捆绑测试,共有 3 个子任务

  • Subtask 1(30 points):|s_1| \leq 15∣s1​∣≤15,|s_2| \leq 5∣s2​∣≤5。
  • Subtask 2(40 points):|s_1| \leq 10^4∣s1​∣≤104,|s_2| \leq 10^2∣s2​∣≤102。
  • Subtask 3(30 points):无特殊约定。

对于全部的测试点,保证 1 \leq |s_1|,|s_2| \leq 10^61≤∣s1​∣,∣s2​∣≤106,s_1, s_2s1​,s2​ 中均只含大写英文字母。

 

题目输出的第二部分不同于书上的next数组或nextval数组,题目要求输出的border意思是,到第i个字符时前后缀的相似度(前缀与后缀有一个长度的相同部分则border【i】=1这样子)。

大话数据结构上的next数组每个位置的值代表的是在当前字符前面的部分前后缀的相似度,而这道题我们要的是包括当前字符以及往前前后缀的相似度,那么我们可以加一个字符a(题目说s1​,s2​ 中均只含大写英文字母所以加一个小写字母不会影响判断),然后输出的值全部向后移动一位,即输出的循环这样写: for(int i=2; i<=l2+1; i++),并且当next【i】不等于0时,要将数据减一,因为k的值是这样来的:如果前后缀一个字符相等,则k=2,两个则k=3,n个则k=n+1。

刚开始写的代码一直ac70:

//ac70
#include<bits/stdc++.h>
using namespace std;

char s[1100000],t[1100000];
int l1,l2;
int nextval[1100000];
int nextt[1100000];
//求模式串T的next函数修正值并存入数组nextval

void get_nextval()
{
    int i=1,k=0;
    nextval[1]=0;
    t[l2+1]='a';
    while(i < l2+1)
    {
        if(k == 0||t[i] == t[k])//t[i]表示后缀的单个字符,t[k]表示前缀的单个字符
        {
            ++i;
            ++k;
            nextt[i]=k;
            if(t[i]!=t[k])
                nextval[i]=k;
            else
                nextval[i]=nextval[k];
        }
        else
            k=nextval[k];
    }
}

void Index_KMP(int pos)
{
    int i=pos,j=1;
    get_nextval();//求到的模式串T的nextval值被存入next数组中
    while(i<=l1 && j<=l2)
    {
        if(j == 0 || s[i] == t[j])
        {
            ++i;
            ++j;
        }
        else
        {
            j=nextval[j];
        }
    }
    if(j > l2)//如果文本串中都已经匹配到t了
    {
        cout<<i-l2<<endl;
        Index_KMP(i-1);
    }
}

int main()
{
    scanf("%s%s",s+1,t+1);//从下标为1的位置开始存
    l1=strlen(s+1);//从下标为一的位置往后算元素个数
    l2=strlen(t+1);
    Index_KMP(1);
    for(int i=2; i<=l2+1; i++)
    {
        if(nextt[i]!=0)
        cout<<nextt[i]-1<<" ";
        else
        cout<<nextt[i]<<" ";
    }
    return 0;
}

错误的两个样例中我下载了一个查看  然后我发现问题在于这里的递归有问题。

该测试样例貌似是文本字符串为1000000个A,模板字符串为1000个A

当前面部分全部相同时,我的代码不能够接着下一个字符比对

然后我就把递归改成了Index_KMP(pos+1),但是我又发现这样子会导致部分样例重复查找到同一个相同部分的情况。

然后我发现转换next数组的方法过于冗杂,可以直接求出borad形式的“next数组”并在与文本字符串匹配时运用,相似度数组输出时能够直接正常输出无需改动后。

因为长度用l1和l2储存了也无需从下标为1开始存储,就直接输入字符串赋值给s和t即可。

那么按照相似的原理就要把 i=1;k=0:变成 i=0;k=-1;原本的next_[1]=0,改为next_[0]=-1,还有if判断中的k==0改为k==-1(都减了一!因为我们要让相似度值往前移动一位所以计算时也要把各个值往前移一位)其他部分无需改变。

void get_next()
{
    int i=0,k=-1;
    next_[0]=-1;
    while(i<l2)
    {
        if(k==-1||t[i]==t[k])
        {
            ++k;
            ++i;
            nextval[i]=k;
            if(t[i]!=t[k])
                next_[i]=k;
            else
                next_[i]=next_[k];
        }
        else
            k=next_[k];//若字符不同,则k值回溯
    }
}

匹配文本串的过程,每当匹配成功一段之后我们还得继续比对判断,这时我们要改变j的值可以避免之前那个代码遇到的问题

最终输出的相似度也无需做任何改变直接输出即可 

 

(其中全局变量设置部分我也进行了改进,因为题目给出的数据范围为1000000,我们要设置多个数组,范围都在1000000以上,每次都写很麻烦,就可以宏定义一个M 1100000,方便程序编写和改动)

ac代码:

#include<bits/stdc++.h>
using namespace std;
#define M 1100000
char s[M],t[M];
int nextval[M];
int next_[M];//nextval的优化数组
int l1,l2;
void get_next()
{
    int i=0,k=-1;
    next_[0]=-1;
    while(i<l2)
    {
        if(k==-1||t[i]==t[k])
        {
            ++k;
            ++i;
            nextval[i]=k;
            if(t[i]!=t[k])
                next_[i]=k;
            else
                next_[i]=next_[k];
        }
        else
            k=next_[k];//若字符不同,则k值回溯
    }
}
void index_KMP()
{
    int i=0;
    int j=0;
    get_next();
    while(i<l1)
    {
        if(j==-1||s[i]==t[j])
        {
            ++i;
            ++j;
        }
        else
            j=next_[j];
        if(j==l2)
        {
            printf("%d\n",i-l2+1);
            j=next_[j];//j值回溯到合适的位置
        }
    }
}
int main()
{
    cin>>s>>t;
    l1=strlen(s);
    l2=strlen(t);
    index_KMP();
    for(int i=1; i<=l2; i++)
        cout<<nextval[i]<<" ";
    return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【模板】KMP字符串匹配 的相关文章

  • 洛谷P1080 [NOIP2012 提高组] 国王游戏

    此题用到的算法有贪心和高精度计算 高精度 高精度真的太折磨人了 xff0c 我搞了好久好久 xff08 PS xff1a python可跳过这一步 xff0c 它自带高精度 一开始我想用long long 但这个数据长度已经超过long l
  • Python中统一快速更换变量的名称

    首先 xff0c 选中需要更改的变量名称 xff0c 其次按下 Ctrl 43 R xff0c 就会出现如下的界面 其次输入你要替换成的变量名字 例如下方截图 xff0c 我要将num替换为str1 最后 xff0c 点击 Replace
  • Linux Qt程序打包

    前言 当我们在linux系统上开发一些工具时 xff0c 想快速分发给相关人员使用时 xff0c 我们可以把开发的相关依赖进行打包 xff0c 然后分发使用 xff0c 其中打包过程中遇到一些问题 xff0c 在没有安装Qt的机器上运行回报
  • word转pdf保持图片清晰度

    今天写论文的遇到两个问题 1 word插入的图片清晰度不够高 xff0c 放大之后不清晰了 2 word里面清晰度高 xff0c 但是转pdf之后放大不清晰了 问题1解决办法 xff1a word中 gt 插入图片 gt 右键选择图片 按照
  • c++字符串连接函数strcat_s

    格式 int a 100 61 0 int b 100 61 0 strcat s a b 功能 把字符数组2 b 连到字符数组1 a 后面 字符数组1必须足够大 连接前两串以 0 结束
  • Python语音合成探究(二、朗读文本的编码问题)

    语音合成时 xff0c 选取的朗读文本大多是网上收集来的TXT 文件 xff0c 有些文件会因为编码原因打开不了 xff0c 程序运行出错 如同样是 离骚 txt 文档 xff0c 用 with open 39 离骚 txt 39 as f
  • 关于Windows上的Android子系统安装

    Win11早些时候的版本公式里展示的安卓系统 Windows Subsystem for Android 简称WSA xff0c 现在可以在电脑中使用 xff0c 过了一年多的时间才想起还有个这种功能 xff0c 在安装时也是发现一些小细节
  • 大一上学期C++课程设计——学生成绩管理系统(QT项目)

    这里是一个大一的萌新 xff01 仅做学习分享 工程文件在评论区置顶 xff01 xff01 近期整理了一下大一上学期的课程设计报告作为学习总结 xff0c 使用的软件是Qt Creator xff0c 主界面效果如下图 QT具体环境如下图
  • 单片机控制直流电机(风扇)电路详解

    单片机引脚为什么无法直接控制电机或风扇 xff1f 我们在使用单片机去控制 43 5V的直流电机或者散热风扇时 xff0c 可能会有一种疑惑 xff0c 51单片机的引脚电压为 43 5V xff0c 为什么不直接用单片机引脚去驱动电机或者
  • [NOIP2002 普及组] 过河卒

    题目描述 棋盘上 AA 点有一个过河卒 xff0c 需要走到目标 BB 点 卒行走的规则 xff1a 可以向下 或者向右 同时在棋盘上 CC 点有一个对方的马 xff0c 该马所在的点和所有跳跃一步可达的点称为对方马的控制点 因此称之为 马
  • Qt学习笔记(5)

    目录 一 菜单栏 MenuBar 二 工具栏 ToolBar 三 状态栏 StatusBar 四 浮动窗口 DockWidget 五 右键菜单 六 托盘菜单 一 菜单栏 MenuBar 只能有一个 创建的最上方 菜单栏有两种方式可以创建 x
  • ftp 命令访问 ftp服务器

    服务端与客户端 登录到FTP服务器时 xff0c 你可以看到服务端的文件 xff0c 这个时候就要有一个区分 xff0c 一个是服务端 xff0c 一个是客户端 xff0c 你发起连接的这台电脑就叫做客户端 xff0c 要连接的FTP服务器
  • day13-面向对象3

    一 私有权限 封装的意义 xff1a 将属性和方法放到一起做为一个整体 xff0c 然后通过实例化对象来处理 xff1b 隐藏内部实现细节 xff0c 只需要和对象及其属性和方法交互就可以了 xff1b 对类的属性和方法增加 访问权限控制
  • 如何在Linux下安装软件,以移植安装libjpeg解码库为例(总结)

    首先 xff0c 从软件官方网站或者其它渠道获取安装软件源码包 xff0c 选择所需软件版本 xff0c 解压放到一个自定义目录下 安装Linux软件通常需要如下三个步骤 xff1a 步骤一 xff1a xff1a configure xx
  • keil5里错误怎么解决Undefined symbol STM32_Control (referred from main.o).

    解决方法 xff1a 遇见这样的问题是忘记添加 xff08 c xff09 文件了 xff0c 如果不知道添加哪个 xff0c 可以根据下面显示的错误点击转到定义文件 xff0c 和 c文件 哪个没有就是缺少哪个文件啦 xff0c 直接添加
  • Ubuntu 安装CUDA

    1 查看操作系统的发行版号和操作系统版本 uname a Linux herri01 HP Z4 G4 Workstation 5 15 0 48 generic 54 Ubuntu SMP Fri Aug 26 13 26 29 UTC
  • 用冒泡法对10个整数从小到大排序。

    第一次学冒泡排序 xff0c 给十个数进行排序 xff0c 我们用到的是冒泡法 xff0c 每次将最大的一个数放到最后 xff0c 由于前九次已经把后面的序列排好 xff0c 所以一共只需要进行九次即可 xff1b 同时在进行第i次排序的时
  • spring boot集成mybatis-plus——Mybatis Plus 批量 Insert_新增数据(图文讲解)

    Mybatis Plus 批量 Insert 新增数据 图文讲解 更新时间 2023 01 10 16 02 58 前言 大家好 xff0c 我是小哈 本小节中 xff0c 我们将学习如何通过 Mybatis Plus 实现 MySQL 批
  • python中输出函数print()的一些必要知识

    一 print基本格式 1 仅用于输出字符串 格式 xff1a print 要输入的字符串 例子 xff1a print 34 希望世界和平 34 2 仅用于输出变量 格式 xff1a print 变量1 xff0c 变量2 xff0c 例
  • 洛谷练习题——入门(顺序结构)P1001、P5703、P5704、P5705、P5706

    有感 xff1a 昨天从同学哪里接触到了洛谷这个刷题的网站 xff0c 感觉对于我这个新手很友好 xff0c 连续刷了几道题 xff0c 有点上头的感觉 xff0c 很有成就感 xff0c 虽然知道这是很简单的题 xff0c 但是对于目前的

随机推荐