cf923D Picking Strings

题目给出的条件:

A->BC

B->AC

C->AB

我们可以通过推理获得三个式子:

A->BB

B<->AB(这意味着B之前可以增加或者减少任意个A)

B<->C(可以把所有的C看成B,因为它们能互相转化)

证明如下:

1.首先有B->AC->AAB   所以B->AAB->AAAAB->AB->AAAB->B

故B<->AB

2.由于B->AAB->AAAC->C

并且C->AB->AAAB->B

故C<->B

3.由证明2,A->BC->BB

那么,问题转化为:能不能从一个只有A和B的序列S-sub,转化到另一个只有A和B的序列T-sub。

1.观察我们证明得到的式子,可以发现我们不能凭空在B后面生成后缀A。

所以,T-sub可以从S-sub 转化来的必要条件是T-sub的后缀A数量大于S-sub的后缀A.

2.由于三个连续的A是可以消除的,所以消除S后缀中多余的A,使得S的后缀A数量恰好不小于T的后缀A数量。(即再消去三个连续的A会导致S的后缀A数量小于T的后缀A数量)

3.再观察式子,发现B可以凭空成双产生(B->AB->BBB),但是不能凭空消失。所以序列T-sub的B数量不得少于S-sub中B的数量。(注意,如果S-sub的后缀A删除了一部分后仍然多于T-sub的后缀A,那么一定要把S-sub中第一个多的A变为BB,此时S-sub中B的数量应该+2考虑,比如S-sub是AAAA,T-sub是BBAA时,S-sub变为ABBAA)

又由于B是成双产生的,所以T-sub和S-sub中的B数量差值是个偶数,否则无法转化。

4.另外,凭空产生B的条件是当S-sub删除一定的末尾A之后不是空串(如果是空串那么无法产生B)

当以上4个条件满足时候,可以从S-sub串转变成T-sub串。具体实现细节参考代码。

#include<bits/stdc++.h>
using namespace std;
char S[100000 + 5], T[100000 + 5];
int Sa[100000 + 5], Sb[100000 + 5], Ta[100000 + 5], Tb[100000 + 5], tailS[100000 + 5], tailT[100000 + 5];
int Q, a, b, c, d;//B可以凭空成双产生
int main()
{
	//freopen("in.txt", "r", stdin);
	//freopen("out.txt", "w", stdout);
	scanf("%s%s", S + 1, T + 1);
	int lenS = strlen(S + 1);
	int lenT = strlen(T + 1);
	scanf("%d", &Q);
	for (int i = 1; i <= lenS; i++) {
		Sa[i] = Sa[i - 1] + (S[i] == 'A');
		Sb[i] = Sb[i - 1] + (S[i] != 'A');
		if (S[i] == 'A')tailS[i] = tailS[i - 1] + 1;
		else tailS[i] = 0;
	}
	for (int i = 1; i <= lenT; i++) {
		Ta[i] = Ta[i - 1] + (T[i] == 'A');
		Tb[i] = Tb[i - 1] + (T[i] != 'A');
		if (T[i] == 'A')tailT[i] = tailT[i - 1] + 1;
		else tailT[i] = 0;
	}
	for (int i = 0; i < Q; i++) {
		scanf("%d%d%d%d", &a, &b, &c, &d);
		int ans = 1;
		int diffb = Tb[d] - Tb[c - 1] - Sb[b] + Sb[a - 1];
		int tS = min(b - a + 1, tailS[b]);
		int tT = min(d - c + 1, tailT[d]);
		if (diffb < 0 || tT - tS>0 || diffb % 2 == 1)ans = 0;
		int remain = (tS - tT) % 3 > 0;
		if (diffb - remain * 2 < 0)ans = 0;
		if (Sb[b] - Sb[a - 1] == 0 && diffb&&tS == tT)ans = 0;
		printf("%d", ans);
	}
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值