191021-KMP算法

191021-KMP算法-处理单模式串问题

模板

#include<bits/stdc++.h>
using namespace std;
int n,m,T,nxt[100009];
char s[100009],t[100009];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s%s",s+1,t+1);
		n=strlen(s+1);
		m=strlen(t+1);
		for(int i=2,j=0;i<=n;i++)//nex[1]=0,所以从2开始计算 
		{
			while(j>0&&s[j+1]!=s[i]) j=nxt[j];
			if(s[j+1]==s[i]) j++;
			nxt[i]=j;
		}
		int ans=0;
		for(int i=1,j=0;i<=m;i++)
		{
			while(j>0&&s[j+1]!=t[i]) j=nxt[j];
			if(s[j+1]==t[i]) j++;
			if(j==n)
			{
				ans++;
				j=nxt[j];
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

作用

因为只需要对模式串进行预处理,因此可以解决给定一个B串和一群不同的A串,问B是哪些A串的子串的问题。

例题 1 洛谷P4391

题目描述

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

输入格式

第一行给出字符串的长度,1 < L ≤ 1,000,000.

第二行给出一个字符串,全由小写字母组成.

输出格式

输出最短的长度

分析

这道题求的是字符串ss最小长度的循环,我们称之为“ss的循环子串”,首先引入结论:

ans=n-next[n]

一、 先求出next数组(其实只求出next[n]就行了),即求出字符串的最大公共前后缀;
假设这两段是整个字符串ss的最大公共前后缀;
第一段: 1~next[n]
第二段: n-next[n]+1~n
为了方便观察,我将前缀和后缀分开,令它们上下一一对应;
二、 现在我们人为地把字符串按照红色段的长度划分为若干段并标号(图中的第1,2,3,4,5,6,7,8,9段);
容易看出,红色的一段和后缀合起来就是原字符串;在这里插入图片描述
所以推出:

  1. 因为上下对应相等,故第1段等于红色段;

  2. 因为是公共前后缀,故第2段等于第1段;

  3. 因为上下对应相等,故第3段等于第2段;

  4. 因为是公共前后缀,故第4段等于第3段;

  5. 红色段就是循环子串;

三、从而,我们知道了原字符串ss除去公共前后缀(图中的黑色段)中的一个剩下的就是循环子串(图中的红色段);同样,易知原串ss除去开头(或结尾)的循环子串(图中的红色段)剩下的部分就是公共前后缀(图中的黑色段)。

至此,问题中要求的原字符串ss的最小循环子串,就转化成了求原字符串ss的最大公共前后缀;

因此,有 ans=n-next[n]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值