【CF】1056E-Check Transcription 题解

传送门:1056E
标签:思维

题目大意

阿卡迪的朋友在一家大型射电望远镜工作。几十年前,该望远镜向遥远星系发送了一个信号 s s s
最近他们收到了回复 t t t,他们认为这是外星人的回应!科学家们现在想要检查信号 t t t 是否与 s s s 类似。原始信号 s s s 是零和一的序列(每个人都知道二进制代码是宇宙通用语言)。返回的信号 t t t 看起来不像 s s s 那么简单,但科学家们不会放弃!他们将 t t t 表示为英文字母的序列,并表示 t t t s s s 类似,如果你能用某个字符串 r 0 r_0 r0 替换 s s s 中的所有零,用另一个字符串 r 1 r_1 r1 替换 s s s 中的所有一,并获得 t t t。字符串 r 0 r_0 r0 r 1 r_1 r1 必须不同且非空。请帮助阿卡迪的朋友找到零和一的可能替换数( r 0 r_0 r0 r 1 r_1 r1 的配对数),将 s s s 转换为 t t t
输入:第一行包含一个字符串 s s s 2 ≤ ∣ s ∣ ≤ 1 0 5 2 \le |s| \le 10^5 2s105),由零和一组成 — 原始信号。第二行包含一个仅由小写英文字母组成的字符串 t t t 1 ≤ ∣ t ∣ ≤ 1 0 6 1 \le |t| \le 10^6 1t106) — 收到的信号。保证 s s s 包含至少一个 ‘0’ 和至少一个 ‘1’。
输出:打印一个整数——将 s s s 转换为 t t t r 0 r_0 r0 r 1 r_1 r1 的配对数。如果没有这样的配对,则打印 0。

算法分析

  • 这题要靠观察得出结论。假设我们知道 r 0 r_0 r0 的长度。由于 ‘0’ 和 ‘1’ 的数量固定并且结果字符串的长度也是固定的,我们知道 r 1 r_1 r1 的长度(或者知道没有合适的整数大小的 r 1 r_1 r1 存在)。此时我们可以暴力求出 r 0 r_0 r0 的长度,计算 r 1 r_1 r1 的长度,然后检查相应的子串是否相等(所以 r 0 r_0 r0 r 1 r_1 r1 可以正确地定义)。
  • 听起来像是 O ( ∣ s ∣ ∣ t ∣ ) O(|s||t|) O(s∣∣t)(候选数少于 ∣ t ∣ |t| t,检查可以在 ∣ s ∣ |s| s 内完成,例如使用哈希表)?并非,实际上是 O ( ∣ t ∣ ) O(|t|) O(t)。首先,注意到我们暴力求解哪个字母并不重要 — 它们中的一个会定义另一个。所以假设我们在暴力求解模式字符串 s s s 中出现次数较多的字母。
  • 我们将它的出现次数记为 c c c,这样显然有至多 ∣ t ∣ c \frac{|t|}{c} ct 个候选。每个候选都在 ∣ s ∣ |s| s 内检查。所以总时间是 ∣ t ∣ c ∣ s ∣ \frac{|t|}{c}|s| cts因为我们使用了更频繁的字母, c ≥ ∣ s ∣ 2 c \geq \frac{|s|}{2} c2s. 因此 ∣ t ∣ c ∣ s ∣ ≤ 2 ∣ t ∣ \frac{|t|}{c}|s| \leq2|t| cts2∣t

代码实现

#include <bits/stdc++.h>
using namespace std;

int n, m;
string s, t;
const int P = 131;
long long L[1000100], H[1000100], tx, ty;
const int mod = 1e9 + 7;
int x, y;

int main(){
	cin >> s >> t;
	n = t.length();
	m = s.length();
	for(int i = 0; i < s.length(); i++){
		if(s[i] == '0') x++;
		else y++;
	}
	H[0] = 1;
	for(int i = 1; i <= n; i++){
		L[i] = (L[i - 1] * P % mod + t[i - 1])%mod;
		H[i] = H[i - 1] * P % mod;
	}
	int ans = 0;
	for(int i = 1; i * x + y <= n; i++){
		if((n - i * x) % y != 0) continue;
		int j = (n - i * x) / y;
		int l = 0;
		bool flag = true, f1 = false, f2 = false;
		for(int k = 0; k < m; k++){
			if(s[k] == '0'){
				if(i + l > n){
					flag = false;
					break;
				}
				if(f1 == false) tx = ((L[i + l] - L[l] * H[i]) % mod + mod) % mod;
				else if(tx !=  ((L[i + l] - L[l] * H[i]) % mod + mod) % mod){
					flag = false;
					break;
				} 
				f1 = true;
				l += i;
			}else{
				if(j + l > n){
					flag = false;
					break;
				}
				if(f2 == false) ty = (L[j + l] - L[l] * H[j] % mod + mod) % mod;
				else if(ty != (L[j + l] - L[l] * H[j] % mod + mod) % mod){
					flag = false;
					break;
				} 
				f2 = true;
				l += j;
			}
		}
		if(flag && tx != ty) ans++;
	}
	cout << ans << '\n';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值