HDU 6230 Palindrome(主席树)

Palindrome

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 329    Accepted Submission(s): 129


Problem Description
Alice like strings, especially long strings. For each string, she has a special evaluation system to judge how elegant the string is. She defines that a string  S[1..3n2](n2)  is one-and-half palindromic if and only if it satisfies  S[i]=S[2ni]=S[2n+i2](1in) .For example,  abcbabc  is one-and-half palindromic string, and  abccbaabc  is not. Now, Alice has generated some long strings. She ask for your help to find how many substrings which is one-and-half palindromic.
 

Input
The first line is the number of test cases. For each test case, there is only one line containing a string(the length of strings is less than or equal to  500000 ), this string only consists of lowercase letters.
 

Output
For each test case, output a integer donating the number of one-and-half palindromic substrings.
 

Sample Input
  
  
1 ababcbabccbaabc
 

Sample Output
  
  
2
Hint
In the example input, there are two substrings which are one-and-half palindromic strings, $abab$ and $abcbabc$.
 

Source

题意:字符串S【1,3n-2】 中以 位置n(C1)和2n-1(C2)为回文串中心,长度都为n,找出给出的主串中有多少这样的子串

分析:首先用manacher求出以每个点为中心的回文串半径,考虑每个点作为C1时,有多少位置作为C2可以组成字符串S,  位置 i 的回文串长度为leni,统计多少第k (i<=k<=i+leni)位置的k-lenk是否小于i。所以我们用主席树实现,每个位置存下权值 i - leni,每次求 第i位置作为C1 时 求出 区间【i,i+leni】中权值小于 i 的数量。

代码:

#include<bits/stdc++.h>
#define maxn 500005
using namespace std;
int f[maxn],len;
int tot,root[maxn*30],ls[maxn*30],rs[maxn*30];
long long sum[maxn*30];
char s[maxn];
void Manacher(){
    len=strlen(s+1);
    int mx=1,p=1;
    s[0]='#';
    for(int i=1;i<=len;i++){
        if(mx>i) f[i]=min(mx-i,f[2*p-i]);
        else f[i]=0;
        while(s[i+f[i]+1]==s[i-f[i]-1]) f[i]++;
        if(f[i]+i>mx){
            mx=f[i]+i;
            p=i;
        }
    }
    return;
}

void update(int k,int pre,int &now,int l,int r){
    now=++tot;
    ls[now]=ls[pre];
    rs[now]=rs[pre];
    sum[now]=sum[pre]+1;
    
    if(l==r){
        return;
    }
    int m=(l+r)>>1;
    if(k<=m) update(k,ls[pre],ls[now],l,m);
    else update(k,rs[pre],rs[now],m+1,r);
}

long long query(int L,int R,int r1,int r2,int l,int r){
    if(L<=l && r<=R){
        //cout<<sum[r1]<<"  "<<sum[r2]<<endl;
        return sum[r1]-sum[r2];
    }
    int m=(l+r)>>1;
    long long cou=0;
    if(L<=m)  cou+=query(L,R,ls[r1],ls[r2],l,m);
    if(R>m)  cou+=query(L,R,rs[r1],rs[r2],m+1,r);
    return cou;
}
int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%s",s+1); 
        Manacher();
        long long ans=0;
        tot=0;
        root[0]=ls[0]=rs[0]=sum[0]=0;
        for(int i=1;i<=len;i++){
            if(f[i])update(i-f[i],root[i-1],root[i],1,len);
            else root[i]=root[i-1];
        }
        for(int i=1;i<=len;i++){
            if(f[i])ans+=query(1,i,root[i+f[i]],root[i],1,len);
            
        }
        cout<<ans<<endl;
    }
     
    
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值