首页 > 技术文章 > ARC060F题解

lmpp 2022-04-27 18:45 原文

诈 骗 题

我一直以为是 DP 什么的有性质,结果发现是道诈骗题,什么性质都没有。

首先特判掉全 \(a\) 串。有一个结论:全场最佳只有可能是两个字符串组成的。

随便证明一下。设 \(S[i]\) 是长度为 \(i\) 的前缀,\(T[i]\) 是长度为 \(i\) 的后缀。

首先对于 \(S[i](2i\geq n)\)\(T[i](n\geq 2i-2)\) 的都不用管,这些肯定不是原串的循环。

那么在我们的讨论中,前缀和后缀的长度不可能超过 \(n\) 的一半。

找到一个分界点 \(i\)。如果 \(S[i]\)\(T[i+1]\) 都是坏的,那么往右或往左移一格一定是好的。

证明:尝试说明若 \(S[i]\) 是原串的循环则 \(S[i+1]\) 一定不是原串的循环。

首先全 \(a\) 串已经被我们判掉了。设一个 \(i=ax,i+1=by\)。那么 \(\gcd(x,y)\) 一定等于 \(1\)

若不等于 \(1\),那么说明 \(\gcd(x,y)\neq 1\),也说明 \(\gcd(i,i+1)\neq 1\),而这不可能。

然后,正反跑两遍 KMP 判断是不是循环就完了。。。

upd:草,居然是对的

#include<cstring>
#include<cstdio>
const int M=5e5+5;
int n,F[M],G[M];char s[M],t[M];
inline bool check(){
	for(int i=2;i<=n;++i)if(s[i]!=s[i-1])return false;return true;
}
inline bool check(int*fail,const int&x){
	if(fail[x]*2<x)return true;return x%(x-fail[x]);
}
signed main(){
	int sum(0);
	scanf("%s",s+1);n=strlen(s+1);if(check())return printf("%d\n1",n),0;for(int i=1;i<=n;++i)t[n-i+1]=s[i];
	for(int j=0,i=2;i<=n;++i){
		while(j&&s[i]!=s[j+1])j=F[j];if(s[i]==s[j+1])++j;F[i]=j;
	}
	for(int j=0,i=2;i<=n;++i){
		while(j&&t[i]!=t[j+1])j=G[j];if(t[i]==t[j+1])++j;G[i]=j;
	}
	if(check(F,n))return printf("1\n1"),0;
	for(int i=1;i<n;++i)if(check(F,i)&&check(G,n-i))++sum;printf("2\n%d",sum);
}

推荐阅读