2017 CCPC 哈尔滨 A (hdu 6230) Palindrome (Manacher + BIT)

8 篇文章 0 订阅
5 篇文章 0 订阅

解题思路:   

题目中要求的回文串大致为  ______ i ____ j ______  分别以 i j 为中心的两个回文串

我们可以使用Manacher算法先处理出以每个字符为中心的回文串的长度,注意这里求出来的回文串长度一定都是奇数

然后我们令 p[i] 为以第 i 个字符为中心的回文半径

然后一个字符串想要满足题意需要满足下面三个条件

j > i
j-i <= p[i]
j-i <= p[j]

我们可以把最后一个式子移项  得到 i >= j - p[j]  ①

所以我们可以每次把 满足①条件的 j 放入树状数组中,然后每次求在 ( i  , i+p[i] ] 范围内的j有多少


AC代码:

/*
* @Author: wchhlbt
* @Last Modified time: 2017-11-12
*/

#include <bits/stdc++.h>

#define inf 0x3f3f3f3f
#define pb push_back
#define AA first
#define BB second
#define ONES(x) __builtin_popcount(x)
#define _  << " " <<
using namespace std;

typedef pair<int, int> P;
typedef long long ll ;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
const double eps =1e-3;
const int mod = 1000000007;
const double PI = acos(-1.0);
inline int read(){ int num;    scanf("%d",&num);   return num;}
const int maxn = 500007;

char Ma[maxn*2];
int Mp[maxn*2];
int Manacher(char s[],int len)
{
    int l = 0;
    Ma[l++] = '$';
    Ma[l++] = '#';
    for(int i = 0; i<len; i++)
    {
        Ma[l++] = s[i];
        Ma[l++] = '#';
    }
    Ma[l] = 0;
    int mx = 0, id = 0;
    for(int i = 0; i<l; i++)
    {
        Mp[i] = mx>i ? min(Mp[2*id-i],mx-i) : 1;
        while(i-Mp[i]>=0 && Ma[i+Mp[i]]==Ma[i-Mp[i]]) Mp[i]++;
        if(i+Mp[i]>mx)
        {
            mx=i+Mp[i];
            id=i;
        }
    }
    return l;
}

char s[maxn];
int p[maxn];
vector<int> g[maxn];

//BIT 向下统计 向上修改
int bit[maxn];
int n;
int sum(int i)
{
    int s = 0;
    while(i>0){
        s += bit[i];
        i -= i & -i;
    }
    return s;
}

void add(int i, int x)//i 不能取 0
{
    while(i<=n){
        bit[i] += x;
        i += i&-i;
    }
}

int main()
{
    int t = read();
    while(t--){
        //init
        memset(bit,0,sizeof bit);
        for(int i = 0; i<maxn; i++) g[i].clear();

        scanf("%s",s);
        int slen = strlen(s);
        int len = Manacher(s,slen);
        int k = 1;
        for(int i = 2; i<len; i+=2){
            p[k] = Mp[i]/2 -1;
            //cout << k _ p[k] _ Mp[i] << endl;
            g[k-p[k]].pb(k);
            k++;
        }
        n = slen;
        ll ans = 0;
        for(int i = 1; i<=n; i++){
            for(int j = 0; j<g[i].size(); j++)
                add(g[i][j],1);
            ans += sum(min(i+p[i],n)) - sum(i);
        }
        cout << ans << endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值