最小表示法

本文介绍了一种用于判断字符串是否具有周期性的高效算法。通过将原串复制并接在自身后面,使用双指针技术进行逐位比较,能够快速找到最小重复单位。算法详细流程包括初始化指针、对比字符直至找到不匹配点,并根据比较结果调整指针位置。最终确定字符串的最小表示起点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模板传送门
参考博客
算法流程:
把原串复制,接在后面。
记两个指针 p , q p,q p,q。初始化 p = 1 , q = 2 p=1,q=2 p=1,q=2
从两个指针开始往后逐位比较。
a [ p + k ] = = a [ q + k ] a[p+k]==a[q+k] a[p+k]==a[q+k],则 + + k ++k ++k
若最后 k = = n k==n k==n,说明原串只由一个字符组成,任意输出即可。
否则此时 k k k满足: a [ p + k ] ! = a [ q + k ] a[p+k]!=a[q+k] a[p+k]!=a[q+k]

a [ p + k ] > a [ q + k ] a[p+k] > a[q+k] a[p+k]>a[q+k],则移动 p p p p = p + k + 1 p=p+k+1 p=p+k+1
若此时 p = = q p==q p==q,则 + + p ++p ++p,避免指针相同。
如果 p > n p>n p>n,那么退出,最小表示的起点即为 q q q

a [ p + k ] < a [ q + k ] a[p+k] < a[q+k] a[p+k]<a[q+k],同理。

那么问题来了,空间为什么不开两倍呢?

#include<bits/stdc++.h>
#define cs const
#define re register
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>
	inline T get(){
		char ch;T x;
		while(!isdigit(ch=gc()));x=ch^48;
		while(isdigit(ch=gc())) x=((x+(x<<2))<<1)+(ch^48);
		return x;
	}
	inline int gi(){return get<int>();}
}
using IO::gi;
cs int N=3e5+10;
int n,a[N<<1],p,q,k;
int main(){
	//freopen("1368.in","r",stdin);
	n=gi();
	for(int re i=1;i<=n;++i) a[n+i]=a[i]=gi();
	p=1,q=2;
	while(p<=n&&q<=n){
		for(k=0;k<n&&a[p+k]==a[q+k];++k);if(k==n) break;
		(a[p+k]>a[q+k])?(p+=k+1,p+=(p==q)):(q+=k+1,q+=(p==q));
	}for(int s=std::min(p,q),i=0;i<n;++i) printf("%d ",a[i+s]);
	return puts(""),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值