1月10日刷题总结

文章讲述了使用KMP算法进行字符串匹配,找出一个字符串在另一个字符串中所有出现的位置。同时,探讨了字符串的border概念,即既是前缀又是后缀的子串,并要求求解每个前缀的最长border长度。在第二个问题中,讨论如何确定一个字符串是由另一不确定字符串自我连接形成的最短长度,同样利用了next数组来找到最长公共前后缀。
摘要由CSDN通过智能技术生成

KMP字符串匹配

题目描述

给出两个字符串 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 中均只含大写英文字母。

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>//模板
int next[1000005];
void kmp(char *str,char *sub)
{
    int i=0,j=0;//i指向主串,j指向子串
    int len1=strlen(str),len2=strlen(sub);
    for(i=0;i<len1;i++)
    {
        while(j>0&&str[i]!=sub[j])//不相等的时候,以及没有回退到0的情况
        {
            j=next[j-1];//回退
        }
        if(str[i]==sub[j])
        {
            j++;//此处只需要j++,i++在for里面
        }
        if(j==len2)//子串完成遍历的时候,说明子串存在于与主串相等的部分
        {
            printf("%d\n",i-j+2);//j存在自增一次,加一个1,下标从0开始,所以再加一个1所以就是+2
        }
    }
}
void getnext(char *sub)//next数组的构建
{
    int i,j=0;
    int len=strlen(sub);
    next[0]=0;//此处也可以省略,next数组为全局数组,默认为0
    for(i=1;i<len;i++)
    {
        while(j!=0&&sub[i]!=sub[j])
        {
            j=next[j-1];//回退
        }
        if(sub[i]==sub[j])
        {
            j++;
        }
        next[i]=j;
    }    
}
int main()
{
    char str[1000005]={'\0'},sub[1000005]={'\0'};
    scanf("%s",str);getchar();
    scanf("%s",sub);
    getnext(sub);
    kmp(str,sub);
    int len=strlen(sub);    
    for(int i=0;i<len;i++)
    {
        printf("%d ",next[i]);
    }
}

Radio Transmission 无线传输

题目描述

给你一个字符串 s_1s1,它是由某个字符串 s_2s2 不断自我连接形成的。但是字符串 s_2s2 是不确定的,现在只想知道它的最短长度是多少。

输入格式

第一行一个整数 LL,表示给出字符串的长度。

第二行给出字符串 s_1s1 的一个子串,全由小写字母组成。

输出格式

仅一行,表示 s_2s2 的最短长度。

输入输出样例

输入 #1复制

8

cabcabca

输出 #1复制

3

说明/提示

样例输入输出 1 解释

对于样例,我们可以利用 \texttt{abc}abc 不断自我连接得到 \texttt{abcabcabc}abcabcabc,读入的 \texttt{cabcabca}cabcabca,是它的子串。

规模与约定

对于全部的测试点,保证 1 < L \le 10^61<L≤106。

思路:找到最长公共前后缀next[len],结论就是len-next[len];

理解:其实很好理解,给你一个字符串 s1,它是由某个字符串 s2 不断自我连接形成的。但是字符串 s2 是不确定的,现在只想知道它的最短长度是多少。 我们仔细读这句话,其实就是s1是由s2的元素构成,其实也就是s1的子串一定有s2,我们只要到寻找最小的子串的长度即可

代码就和k'm'p中求解next一样

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
int next[1000005];
void getnext(char *sub)
{
    int len=strlen(sub);
    int j=0;
    for(int i=1;i<len;i++)
    {
        while(j!=0&&sub[i]!=sub[j])
        {
            j=next[j-1];//回退
        }
        if(sub[i]==sub[j])
        {
            j++;
        }
        next[i]=j;
    }
}
int main()
{
    char sub[1000005];
    int n;
    scanf("%d",&n);
    getchar();//注意这里getchar
    scanf("%s",sub);
    int len=strlen(sub);
    getnext(sub);
    printf("%d",len-next[len-1]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡卡卡卡罗特

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值