3798. 【NOIP2014模拟8.22】临洮巨人 (Standard IO)

 

Time Limits: 1000 ms  Memory Limits: 262144 KB  Detailed Limits  

Description

Input

一行一个由大写字母A到L组成的字符串S。

Output


注意是输出子串的个数。

Sample Input

ABACABA

Sample Output

2
样例解释:BAC和CAB
 

Data Constraint

对于30%的数据,|S|<=100。
对于70%的数据,|S|<=1000。
对于100%的数据,1<=|S|<=1000000。

Source / Author: 常州高级中学NOIP2014夏令营 lintaojuren

 

 

反思:

比赛的时候就伪AC了, 自以为万无一失(打了两个程序, 一个暴力优化, 一个Ac版, 已经对了拍), 可两个程序都是错的, 错的一起错。

这个故事告诉我们对拍程序一定要是纯暴力程序。

思路 : 

回想之前做过类似的题目, 对于要求两种元素在一段区间内数目相当, 我们把其中一种元素设为1, 另一个元素设为-1, 做一遍前缀和, 若i ~ j合法, 则pre[j] - pre[i - 1] = 0

可是现在我们有三种元素了。

那么把问题拆开 , 分别讨论ab和ac的相同段。

 

浅层思考, 对于结尾i, 把ab和ac相同段的开头列出, 若有相同, 则a=b=c。

可这样太慢了, 空间也不允许。

瓶颈在于不能利用前面已经获得的信息。

那么我们能不能对于结尾i, 找到距离i最近的j使其a = b = c, 然后利用j的信息呢?

可以。设cnt[i]为到i有多少a=b=c端点, cnt[i] = cnt[j] + 1, ans += cnt[i]。

时间复杂度比正解稍慢。

#include<bits/stdc++.h>
#define open(x) freopen(x".in", "r", stdin);freopen(x".out", "w", stdout)
#define mem(a, b) memset(a, b, sizeof(a))
#define mcy(a, b) memcpy(a, b, sizeof(a))
#define pf printf
#define sf scanf
#define fo(i, a, b) for(register int i = a; i <= b; ++i) 
#define fown(i, a, b) for(register int i = a; i >= b; --i)
#define em(p, x) for(ll p=tail[x];p;p=e[p].fr)
#define ll long long
#define wt(a, c, d, s) fo(i, c, d) pf((s), a[i]); puts("")
#define rd(a, c, d) fo(i, c, d) in(a[i])
#define N 2000010
#define inf 2147483647
#define mod (ll)(1e9 + 7)
using namespace std;

template<class T> 
T in(T &x) {
	x=0;
	char ch = getchar(); ll f = 0;
	while(ch < '0' || ch > '9') f |= ch == '-', ch = getchar();
	while(ch >= '0' && ch <= '9') x = (x<<1) + (x<<3) + ch - '0', ch = getchar();
	x = f ? -x : x;
	return x;
}

const ll fix = 1000001;

int n;
int lst1[N], lst2[N];
ll f1[N], f2[N], ans, pre1[N], pre2[N], cnt[N];
char s[N];

int main() {
	open("gaint");
	sf("%s\n", s + 1);
	n = strlen(s + 1);
	mem(lst1, 255), mem(lst2, 255);
	f1[0] = f2[0] = fix;
	lst1[f1[0]] = lst2[f2[0]] = 0;
	pre1[0] = 0, pre2[0] = 0;
	fo(i, 1, n) {
		f1[i] = f1[i - 1], f2[i] = f2[i - 1]; 
		if(s[i] == 'A')
		
			f1[i]++, f2[i]++;
			
		else if(s[i] == 'B') 
			f1[i]--;
		else if(s[i] == 'C')
			f2[i]--;
		int p = lst1[f1[i]], q = lst2[f2[i]];
		while(p >= 0 && q >= 0) {
			if(p == q) break;
			if(i - p > i - q) {
				if(pre2[q] >=0)
				q = pre2[q]; else break;
			} else {if(pre1[p] >= 0)p = pre1[p]; else break;}
		}
		if(p == q && p >= 0) {
			cnt[i] = cnt[p] + 1;
			ans += cnt[i]; //add the ans before
		}
		pre1[i] = lst1[f1[i]], pre2[i] = lst2[f2[i]];
	
		lst1[f1[i]] = i, lst2[f2[i]] = i;
	}
	
	printf("%lld\n", ans);
	return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值