BZOJ4377: [POI2015]Kurs szybkiego czytania

本文解析了一道BZOJ题目,利用KMP算法和数论知识解决大规模字符串匹配问题。通过对给定参数生成特定01串,采用裴蜀定理、等差数列及扫描线算法,巧妙地避免了直接生成长串,高效计算子串出现次数。

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

BZOJ

题意

给你三个参数 n , a , p n,a,p n,a,p,你能用这个三个参数生成一个长度为 n n n 01 01 01 c c c,再给你一个长度为 m m m 01 01 01 b b b,问 b b b c c c中出现多少次,可以重叠;

题解

如果我们能按照他给的方法生成串 c c c,那么可以跑个kmp什么的得到答案,但是有一个问题, n n n非常大,我们没有办法把串 c c c生成出来;
那么我们换一种方式,设 F i = ( a ∗ i + b ) % n F_i=(a*i+b)\%n Fi=(ai+b)%n, b i = F i &lt; P ? 0 : 1 , b_i=F_i&lt;P?0:1, bi=Fi<P?0:1,因为有 G C D ( a , n ) = 1 GCD(a,n)=1 GCD(a,n)=1,那么 n n n F i F_i Fi的值互不相同,用裴蜀定理可以证明;再观察这个 F F F,它其实是一个等差数列,公差为 a a a,如果我们确定了第一项在 c c c中的位置,后面的就都确定了;那么我们现在有了一种新的思路,找出那些位置作为第一项是合法的;考虑这么两个不等式,如果 b b b串中第 p p p个位置是 0 0 0的话有 0 ≤ F 1 + a ∗ ( p − 1 ) &lt; P 0\leq F_1+a*(p-1)&lt;P 0F1+a(p1)<P,是 1 1 1的话有 P ≤ F i + a ∗ ( p − 1 ) ≤ n − 1 P\leq F_i+a*(p-1) \leq n-1 PFi+a(p1)n1,通过移项,我们就能得到对 F 1 F_1 F1进行限制的范围,但是只知道那些位置可以的话,我们并不好计算可行的第一项位置,不过我们可以反过来计算不可行的区间,然后用扫描线求出区间并,总长度减去这些不可行的位置,就能得到最终的答案了,不要忘记后面 m − 1 m-1 m1个位置是绝对不可行的;

#include<bits/stdc++.h>
#define Fst first
#define Snd second
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
typedef unsigned int UI;
typedef unsigned long long ULL;
template<typename T> inline void read(T& x) {
	char c = getchar();
	bool f = false;
	for (x = 0; !isdigit(c); c = getchar()) {
		if (c == '-') {
			f = true;
		}
	}
	for (; isdigit(c); c = getchar()) {
		x = x * 10 + c - '0';
	}
	if (f) {
		x = -x;
	}
}
template<typename T, typename... U> inline void read(T& x, U& ... y) {
	read(x), read(y...);
}
const int N=2e6+10;
int n,a,b,m,P,cnt;
char S[N];
struct Data {
	int p,w;
	Data () {}
	Data (int a,int b) :p(a),w(b) {}
	bool operator <(const Data& rhs) const {
		return p<rhs.p;
	}
}A[N<<2];
void ADD(int l,int r) {
	A[++cnt]=Data(l,1); A[++cnt]=Data(r+1,-1);
}
int main() {
	read(n,a,b,P,m); scanf("%s",S+1);
	for(int i=1,t=0;i<=m;++i,(t+=a)%=n) {
		if(S[i]=='0') {
			if(t<=P) ADD(P-t,n-1-t);
			else ADD(n-t+P,n-1),ADD(0,n-1-t);
		}
		else {
			if(t<P) ADD(0,P-1-t),ADD(n-t,n-1);
			else ADD(n-t,n+P-t-1);
		}
	}
	for(int i=n-m+1;i<n;++i) ADD((1ll*a*i+b)%n,(1ll*a*i+b)%n);
	sort(A+1,A+cnt+1); A[++cnt]=Data(n,0);
	int now=0,res=0;
	for(int i=1;i<=cnt;++i) {
		if(A[i].p!=A[i-1].p&&!now) res+=A[i].p-A[i-1].p;
		now+=A[i].w;
	}
	printf("%d\n",res);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值