Noi2016 D1 T1 优秀的拆分 90做法

欢迎访问博客 oi.self-jqe.win

题目大意:

如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆分是优秀的。

例如,对于字符串 aabaabaa,如果令 A=aabA=aabB=aB=a,我们就找到了这个字符串拆分成 AABBAABB 的一种方式。

一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=aA=aB=baaB=baa,也可以用 AABBAABB 表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。

现在给出一个长度为 nn 的字符串 SS,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

做法:

我们来考虑,我们可以通过hash值判定来O1的比较两个子串是否相等,那我们可以立马想到一n^3的做法,不断的去找一个点来枚举并判断,这可以过80分,那么我们可以做一些小的改进,我们用cnt[i]表示字符串第[i,n]位能构成几种AA形式的方案。这个cnt我们可以在平方级别内来预处理出来。然后我们去枚举一个位置,暴力去找他前面的部分能构成几个AA的形式,然后与cnt[i]相乘并加入ans即可。

Ac代码

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
ll mo1 = 951413,mo2 = 980321,jz = 30;
int T,lenn;
ll ans = 0;
ll jl1[31000],jl2[31000],cnt[31000],ijz[31000][2];
char s[31000];
void hash_init()
{
	for (int i = 1;i <= lenn;i++)
	{
		jl1[i] = (jl1[i - 1] + (s[i] - 'a' + 1) * ijz[i][0]) % mo1;
		jl2[i] = (jl2[i - 1] + (s[i] - 'a' + 1) * ijz[i][1]) % mo2;
	}
}
bool check(int l1,int r1,int l2,int r2)
{ 
	if ((r1 - l1) != (r2 - l2)) return 0;
//	printf("%d %d %d %d %lld %lld\n",l1,r1,l2,r2,((jl1[r1] - jl1[l1 - 1]) * ijz[l2 - l1 + 1][0]) % mo1,(jl1[r2] - jl1[l2 - 1]) % mo1);
	int a1 = ((jl1[r1] - jl1[l1 - 1]) * ijz[l2 - l1 + 1][0]) % mo1;
	int a2 = (jl1[r2] - jl1[l2 - 1]) % mo1;
	int b1 = ((jl2[r1] - jl2[l1 - 1]) * ijz[l2 - l1 + 1][1]) % mo2;
	int b2 = (jl2[r2] - jl2[l2 - 1]) % mo2;
	while (a1 < 0) a1 += mo1;
	while (a2 < 0) a2 += mo1;
	while (b1 < 0) b1 += mo2;
	while (b2 < 0) b2 += mo2;
	if (a1 != a2) return 0;
	if (b1 != b2) return 0;
//	printf("true\n");
	return 1;
}
void cnt_init()
{
	for (int i = 1;i < lenn;i++)
		for (int j = 2;i + j - 1<= lenn;j += 2)
			if (check(i,i + (j >> 1) - 1,i + (j >> 1),i + j - 1)) cnt[i]++;
}
void solve()
{
	for (int i = 1;i < lenn;i++)
	{
		int tp = 0;
		for (int j = 2;i - j + 1 >= 1;j += 2)
			if (check(i - j + 1,i - j / 2,i - j / 2 + 1,i)) tp++; 	
		ans += tp * cnt[i + 1];
	}
}
int main()
{
	scanf("%d",&T);
	ijz[1][0] = ijz[1][1] = 1;
	for (int i = 2;i <= 30000;i++)
	{
		ijz[i][0] = ijz[i - 1][0] * jz % mo1;				
		ijz[i][1] = ijz[i - 1][1] * jz % mo2;
	}
	for (int mi = 1;mi <= T;mi++)
	{
		ans = 0;
		memset(cnt,0,sizeof(cnt));
		memset(jl1,0,sizeof(jl1));
		memset(jl2,0,sizeof(jl2));
		scanf("%s",s + 1);
		lenn = strlen(s + 1);
		hash_init();
		cnt_init();
		solve();
		printf("%lld\n",ans);
		int a,b,c,d;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值