bzoj2084/luoguP3501 [Poi2010]Antisymmetry(回文自动机+dp)
阅读原文时间:2023年07月08日阅读:2

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\n",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();}