P3501 [POI2010]ANT-Antisymmetry

传送门

哈希 or Manacher

首先有一个很显然的结论

对于一个回文串s

每次从s的中心开始向左右扩展一步

每次扩展的串一定都是回文串

如 s=abccba

从s的中心左右扩展一步得到 cc

扩展两步得到 bccb

扩展三步就得到了 abccba = s

所以如果我们枚举中心

向左右扩展,找到最长的回文串 s

那么只要 ans += len_s/2 就能得到所有的回文子串的数量

找最长长度要用二分

判断回文的话用哈希就行了

取反的问题在预处理后缀哈希时就可以搞定了

具体实现还是看代码吧

复杂度O(n log n)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef unsigned long long ull;//自然溢出
const int N=1e6+7;
const int base=23333;
int n;
long long ans;//答案一定要用long long 存!!!
ull h1[N],h2[N],fac[N];//前缀哈希,后缀哈希,fac[i]存base^i
char ch[N];
int main()
{
    cin>>n;
    scanf("%s",ch+1);
    fac[0]=1;
    for(int i=1;i<=n;i++)
    {
        fac[i]=fac[i-1]*base;
        h1[i]=h1[i-1]*base+(ch[i]=='1');//预处理前缀哈希
    }
    for(int i=n;i;i--)
        h2[i]=h2[i+1]*base+(ch[i]=='0');//后缀哈希预处理时取反
    for(int i=1;i<n;i++)//枚举中点
    {
        int l=0,r=min(i,n-i),mid;//二分长度
        while(l<r)
        {
            mid=l+ ((r-l)>>1) +1;
            ull hs1=h1[i+mid]-h1[i-mid]*fac[mid*2],hs2=h2[i-mid+1]-h2[i+mid+1]*fac[mid*2];
            //取出子串哈希值
            if(hs1==hs2) l=mid;
            else r=mid-1;
        }
        ans+=l;//更新答案
    }
    cout<<ans;
    return 0;
}
哈希解法

 

然后讲讲Manacher(应该都懂Manacher吧..)

预处理完字符串后,就只要找以 '#' 为中心的回文就好了

我们可以得到每个以 '#' 为中心的最长回文长度,然后就可以像哈希的方法一样更新答案了

复杂度O(n)

 

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=1e6+7;
int n,a[N<<1],f[N],mx,pos;
long long ans;
char s[N];
int main()
{
    cin>>n;
    scanf("%s",s+1);
    n=n*2+1;
    for(int i=1;i<=n;i++)
    {
        if(i&1) a[i]=-1;//相当于'#'
        else a[i]=s[i>>1]-'0';
        //就是爱把字符转数字..
    }
    for(int i=1;i<=n;i++)
    {
        if(a[i]!=-1) continue;//只要找以'#'为中心的回文
        if(i<mx) f[i]=min(mx-i,f[(pos<<1)-i]);
        else f[i]=1;//Manacher核心
        while(1)
        {
            if(a[i+f[i]]==-1)
            {
                f[i]++;
                continue;
                //对'#'特判一下
            }
            if(a[i+f[i]]==a[i-f[i]] || i-f[i]<1 || i+f[i]>n) break;
            f[i]++;//暴力判断
        }
        if(i+f[i]>mx) mx=i+f[i],pos=i;
        ans+=((f[i]-1)>>1);//更新
    }
    cout<<ans;
    return 0;
}
Manacher解法

 

posted @ 2018-09-13 13:35  LLTYYC  阅读(182)  评论(0编辑  收藏  举报