字符串算法·哈希·题解【反对称·Antisymmetry】


洛谷题目链接
WOJ题目链接

题目

洛谷 [POI2010]P3501 ANT-Antisymmetry
WOJ#3818 反对称 Antisymmetry

题目描述

对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。现在给出一个长度为N的01字符串,求它有多少个子串是反对称的。

输入

第一行一个正整数 n。第二行一个长度为 n 的 0/1 字符串。

输出

一行一个整数,表示原串的反对称子串个数。

样例

  • 输入样例
    8
    11001011
  • 输出样例
    7

题意

定义一种01子串,其中每个位置上的值,与它关于中央对称后的位置上的值相反。求给出的字符串中,这种子串的个数。

思路

首先我们来研究一下这种子串的性质。由于0/1始终不能与自己相反,故子串并不存在中央位置,也就是说,子串长度必为偶。其次,由题目分析可知每个位置上的值都与它关于中央对称的位置上的值相反。然后……

嘛,先不管那么多啦!既然没什么思路(误),那我们先愉快地考虑暴力吧!说不定不知什么时候就想出正解了呢!
既然对称的两点相反,那我们就枚举中点,再判断就行了。
以下,便是博主的第一份暴力代码的核心程序明知道过不到偏就要交上去玩的 。不过别这么说嘛,暴力也很考技术的(误)。

for(int i=1;i<n;i++)
{
	l=i,r=i+1;
	while(l>=1&&r<=n)
	{
		if(a[l]==(a[r]^1)) {ans++;l--,r++;}
		else break;
	}
}

不得不说,这份代码的效果远高于博主的预期。在下原以为只能过那么……五六个点吧,结果过了四十九个!剩下的全T了——谁叫一共有六十二个点呢。

不过在暴力的过程中,我们也产生了一些新思路——如果用哈希来判断,能否更优?这样的话,考虑到中点的这样的一个状况,就需要向左和向右分别处理两次哈希,将 s [ i ] s[i] s[i]的哈希值分别存于 h a s h l [ i ] hash_l[i] hashl[i] h a s h r [ i ] hash_r[i] hashr[i]内。
为了进一步优化,在循环过程中可以采用二分答案。那么这道题也就这样解决啦。

代码

#include<iostream>
#include<cstring>
#include<algorithm>
const int maxn=5e5+10;
const unsigned long long base=1e9+7;
using namespace std;
int n,ans;
unsigned long long power[maxn],hash_l[maxn],hash_r[maxn];
char s[maxn];
bool check(int l,int r,int x)
{
	unsigned long long s1,s2;
	int t1=l+x-1,t2=r+x-1;
	s1=hash_l[t1]-hash_l[l-1];
	s2=hash_r[t2]-hash_r[r-1];
	if(l>r)
	{
		swap(s1,s2);
		swap(l,r);
	}
	s1*=power[r-l];
	if(s1==s2) return 1;
	else return 0;
}
int main()
{
	cin>>n;
	cin>>s;
	power[0]=1;
	for(int i=1;i<=n;i++) power[i]=power[i-1]*base;
	for(int i=1;i<=n;i++) hash_l[i]=hash_l[i-1]+s[i-1]*power[i];//从左向右的哈希
	for(int i=1;i<=n;i++) hash_r[i]=hash_r[i-1]+(s[n-i]^1)*power[i];//从右向左的哈希
	for(int i=2;i<=n;i++)
	{
		int k=n-i+2,l=1,r=min(n-i+1,i-1);
		int tot=0;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			if(check(i,k,mid)) tot=max(mid,tot),l=mid+1;
			else r=mid-1;
		}//二分答案
		ans+=tot;
	}
	cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值