【Ybt OJ】[字符串算法 第3章] KMP 算法

「 「 字符串算法 」 」 3 3 3 K M P KMP KMP 算法
目录:

A.子串查找
B.重复子串
C.周期长度和
D.子串拆分

A . A. A. 例题 1 1 1 子串查找

在这里插入图片描述

分析:

一道比 K M P KMP KMP模板还模板的题 人家 K M P KMP KMP模板还要求一个 b o r d e r border border 这个直接匹配就行了

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue> 
//#pragma GCC optimize(2)
#define reg register 
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e6+5;
int lena,lenb,ans,j,qwq[N];
char a[N],b[N];
int main(){
    
    
	scanf("%s%s",a+1,b+1);
	lena=strlen(a+1);
	lenb=strlen(b+1);
	j=0;
	for(int i=2;i<=lenb;i++)
	{
    
    
		while(j&&b[i]!=b[j+1]) j=qwq[j];  //KMP模板
		if(b[i]==b[j+1]) j++;
		qwq[i]=j;
	}
	j=0;
	for(int i=1;i<=lena;i++)
	{
    
    
		while(j&&a[i]!=b[j+1]) j=qwq[j];
		if(a[i]==b[j+1]) j++;
		if(j==lenb) ans++;
	}
	printf("%d",ans);
	
	return 0;
}

B . B. B. 例题 2 2 2 重复子串

在这里插入图片描述

分析:

我们做完 K M P KMP KMP后 就代表 1 1 1 ~ q w q n qwq_n qwqn n − q w q n + 1 n-qwq_n+1 nqwqn+1 ~ n n n是相匹配
所以如果 n ≡ 0 ( m o d ( n − q w q n ) ) n≡0(mod(n-qwq_n)) n0(mod(nqwqn)) 也就是 n n n m o d mod mod ( n − q w q n ) = 0 (n-qwq_n)=0 (nqwqn)=0 则存在重复连续子串
这个子串的长度为 n − q w q n n-qwq_n nqwqn 循环次数 n / ( n − q w q n ) n/(n-qwq_n) n/(nqwqn) 也就是题目要求的数量了

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue> 
//#pragma GCC optimize(2)
#define reg register 
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e6+5;
int lena,ans,qwq[N],j;
char a[N];
int main(){
    
    
	scanf("%s",a+1);
	lena=strlen(a+1);
	while(lena!=1||a[1]!='.')
	{
    
    
		j=0;
		for(int i=2;i<=lena;i++)
		{
    
    
			while(j&&a[i]!=a[j+1]) j=qwq[j];  //KMP
			if(a[i]==a[j+1]) j++;
			qwq[i]=j;
		} 
		if(lena%(lena-qwq[lena])==0) 
			printf("%d\n",lena/(lena-qwq[lena]));  //结论
		else printf("1\n");
		scanf("%s",a+1);
		lena=strlen(a+1);	
	}
	return 0;
}

C . C. C. 例题 3 3 3 周期长度和

在这里插入图片描述

分析:

大概意思是让你找到一个子串 复制一遍后包含原串 找这个子串长度最长
把原串分成两部分 让前面的包含后面的 周期就是前面的子串 ( ( (对着题面理解一下 ) ) )
这其实就是个 K M P KMP KMP
然后要使周期长 那么前面子串就要长 后面的就尽量短 而后面的子串是 K M P KMP KMP长度
想让 K M P KMP KMP小 就是要找匹配少的 q w q i = 0 qwq_i=0 qwqi=0 i i i最小的 要找的就是这个
这个递归 q w q i qwq_i qwqi 0 0 0 就可以用并查集优化了 把每个前缀 i i i保存下来累计

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue> 
//#pragma GCC optimize(2)
#define reg register 
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e6+5;
ll n,j,qwq[N],ans;
char a[N];
ll find(int x){
    
    return (qwq[x])?qwq[x]=find(qwq[x]):x;}  //并查集维护
int main(){
    
    
	scanf("%lld",&n);
	scanf("%s",a+1);
	j=0;
	for(reg int i=2;i<=n;i++)
	{
    
    
		while(j&&a[i]!=a[j+1]) j=qwq[j];
		if(a[i]==a[j+1]) j++;  //KMP
		qwq[i]=j;
	}
	for(reg int i=1;i<=n;i++)
		ans+=i-find(i);  //统计
	printf("%lld",ans);
	return 0;
}

D . D. D. 例题 4 4 4 子串拆分

在这里插入图片描述

分析:

数据不大 直接跑 n 2 n^2 n2算法 枚举两边范围
看能否组成 A B A ABA ABA形 可以跑 K M P KMP KMP 直到两范围没有重合即可
这样做出来的 A A A是最长的 但一个串 S S S会有多种分配方法 要找其他的 就继续使 q w q i = i qwq_i=i qwqi=i
遇到一组不重合的就可以了 要不重合 就要使 q w q n ∗ 2 < n qwq_n*2<n qwqn2<n 指针一直回跳就行
这样是 n 3 n^3 n3的 然后先找出端点 端点就会在 K M P KMP KMP后包含 就不用枚举了

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue> 
//#pragma GCC optimize(2)
#define reg register 
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=2e4+5;
int n,m,qwq[N],ans,j,p;
char ch[N],d[N];
int main(){
    
    
	scanf("%s",ch+1);
	scanf("%d",&p);
	n=strlen(ch+1);
	for(reg int i=1;i<=n;i++)
	{
    
    
		memset(d,0,sizeof(d));
		memset(qwq,0,sizeof(qwq));
		for(reg int l=1;i+l-1<=n;l++)
			d[l]=ch[i+l-1];  //处理左端点
		m=strlen(d+1); j=0;
		for(reg int l=2;l<=m;l++)
		{
    
    
			while(j&&d[l]!=d[j+1]) j=qwq[j];  //KMP
			if(d[l]==d[j+1]) j++;
			qwq[l]=j;
		}
		j=0;
		for(reg int k=1;k<=m;k++)
		{
    
    
			while(j&&d[k]!=d[j+1]) j=qwq[j];
			if(d[k]==d[j+1]) j++;
			while((j<<1)>=k) j=qwq[j];  //不合法
			if(j>=p) ans++;  //满足要求
		}
	}
	printf("%d",ans);	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/dgssl_xhy/article/details/113133483