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 解释
![](https://i-blog.csdnimg.cn/blog_migrate/ae477086c37b94e26fa744f403325795.jpeg)
。
对于 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]);
}