bzoj2084/luoguP3501 [Poi2010]Antisymmetry(回文自动机+dp)

bzoj2084/luoguP3501 [Poi2010]Antisymmetry(回文自动机+dp)

bzoj Luogu

对于一个01字符串,如果将这个字符串0和1取反后,再将整个串反过来和原串一样,就称作“反对称”字符串。比如00001111和010101就是反对称的,1001就不是。

现在给出一个长度为N的01字符串,求它有多少个子串是反对称的。

题解时间

这玩意咋看都像是回文串不是嘛。

然后与此同时还是经典计数问题。

所以考虑能不能以这里面的这个规则写个PAM

发现还真能搞:

首先这种串只有偶串,所以不建奇方向的(跳到奇根1直接跳出)

此外再把match改一下

完事了。

#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
namespace RKK
{
const int N=500011;
char str[N];int n;
struct remilia{int tranc[2],len,fail;};
struct sakuya
{
	remilia p[N];
	int fin,size;
	int val[N];
	sakuya()
	{
		fin=0,size=1;
		p[0].len=0,p[1].len=-1;
		p[0].fail=p[1].fail=1;
	}
	int match(char *s,int i,int px){return p[px].len==-1||(i-p[px].len-1!=0&&s[i-p[px].len-1]!=s[i]);}
	void ins(char *s,int i)
	{
		int ch=s[i]-'0';
		int npx,lpx,lpy;
		lpx=fin;
		while(!match(s,i,lpx)) lpx=p[lpx].fail;
		if(i-p[lpx].len-1==0||s[i-p[lpx].len-1]==s[i]){fin=0;return;}
		if(!p[lpx].tranc[ch])
		{
			npx=++size;
			p[npx].len=p[lpx].len+2;
			lpy=p[lpx].fail;
			while(!match(s,i,lpy)) lpy=p[lpy].fail;
			p[npx].fail=p[lpy].tranc[ch];
			p[lpx].tranc[ch]=npx;
		}
		fin=p[lpx].tranc[ch];
		val[fin]++;
	}
	void work()
	{
		lint ans=0;
		for(int i=size;i>=2;i--) val[p[i].fail]+=val[i],ans+=val[i];
		printf("%lld
",ans);
	}
}pam;
int Iris()
{
	scanf("%d%s",&n,str+1);
	for(int i=1;i<=n;i++) pam.ins(str,i);
	pam.work();
	return 0;
}
}
int main(){return RKK::Iris();}
原文地址:https://www.cnblogs.com/rikurika/p/12079220.html