bzoj2084 hash||manacher

题意:给一个01串,现在定义一个新的回文方式为0和1相等,而00,11不等。求有多少子串满足这种新的回文方式。
思路:求多少子串其实就是求每个点最大回文串半径。
manacher很好写O(n)
hash的话,我们计算两个哈希值,一个s的一个翻转s后01再反转的哈希值。
之后二分判断即可。

manacher:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int maxn = 5e5+5;

char ma[maxn<<1];
int mp[maxn<<1];

void manacher(string s,int len){ 
    int p = 0,r = 0,mid = 0;
    ma[p++] = '$',ma[p++] = '#';
    forn(i,len) ma[p++] = s[i],ma[p++] = '#';
    forn(i,p){
        mp[i] = r>i?min(mp[(mid<<1)-i],r-i):1;
        if(ma[i]=='#')while(1){
            if(ma[i+mp[i]]=='0'&&ma[i-mp[i]]=='1') mp[i]++;
            else if(ma[i+mp[i]]=='1'&&ma[i-mp[i]]=='0') mp[i]++;
            else if(ma[i+mp[i]]=='#') mp[i]++;
            else break;
        }
        if(i+mp[i]>r) r = mp[i]+i,mid = i;
    }
}

int main(){ 
    IO; 
    int n;cin>>n;
    string s;cin>>s;
    int len = s.size();
    manacher(s,len);
    int ans = 0;
    //forn(i,(len+1)<<1) cerr<<ma[i];
    //cerr<<'\n';
    forn(i,(len+1)<<1){
      //  cerr<<i<<' '<<mp[i]<<'\n';
        ans+=mp[i]/2;
    }
    cout << ans<<'\n';
    return 0;
}

hash:

#include <bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int maxn = 5e5+5;
const int seed = 13331;
ull S[maxn<<1],S2[maxn<<1],P[maxn<<1];
int n,len;string s;

void getss(){
    int len = s.size();
    string t = "";t+='#';
    forn(i,len) t+=s[i],t+='#';
    s = t;
}

bool check(int l,int p){
    ull x,y;
    int p2 = len-p+1;
    x = S[p]-S[p-l]*P[l];
    y = S2[p2]-S2[p2-l]*P[l];
    return x==y;
}

int main(){
    IO;
    P[0] = 1;
    for1(i,(maxn<<1)-1) P[i] = P[i-1]*seed;
    cin>>n;cin>>s;
    getss();
    len = (n<<1)+1;
    //cerr<<s<<'\n';
    for1(i,len) S[i] = S[i-1]*seed+s[i-1];
    reverse(s.begin(),s.end());
    forn(i,len) {
        if(s[i]=='0') s[i] = '1';
        else if(s[i]=='1') s[i] = '0';
    }
    for1(i,len) S2[i] = S2[i-1]*seed+s[i-1];
    long long ans = 0;
    //cerr<<s<<'\n';
    for(int i = 2;i<len;i++){
        int l = 1,r = min(i+1,len-i),res = 1;
        while(l<=r){
            int mid = l+r>>1;
            if(check(mid,i)){
                l = mid+1;
                res = mid;
            }else r = mid-1;
        }
        //cerr<<s[i-1]<<' '<<res<<'\n';
        ans += res/2;
    }
    cout << ans <<'\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值