2017中国大学生程序设计竞赛-哈尔滨站 A - Palindrome

Palindrome

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


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
/*************************************************************************
    > File Name: A.cpp
    > Author: LyuCheng
    > Created Time: 2017-12-01 21:54
    > Description: 
        题意:给你一个字符串问你有多少个子串满足s[i]=s[2*n-i]=s[2*n+i-2]

        思路:满足条件的子串实际上是关于两点回文的一个子串,并且这个回文串
               一定是奇数回文,用马拉车算法求出以每个点的回文半径,如果满足题
            目的条件,设i<j是这个子串的两个回文点,那么i-j<=pos[i]&&i-j<=pos[j]
            用树状数组记录一下,然后统计
 ************************************************************************/
#include <bits/stdc++.h>

#define MAXN 567890
#define lowbit(x) x&(-x)
#define LL long long

using namespace std;

int t;
int n;
char str[MAXN<<1];
char tmp[MAXN<<1];
int Len[MAXN<<1];
int pos[MAXN<<1];
int c[MAXN<<1];
LL res;
vector<int>v[MAXN<<1];

inline void update(int x){
    while(x<MAXN){
        c[x]++;
        x+=lowbit(x);
    }
}

inline int getsum(int x){
    int s=0;
    while(x>0){
        s+=c[x];
        x-=lowbit(x);
    }
    return s;
}

int cal(char *st){
    int i,len=strlen(st);
    tmp[0]='@';//字符串开头增加一个特殊字符,防止越界
    for(i=1;i<=2*len;i+=2){
        tmp[i]='#';
        tmp[i+1]=st[i/2];
    }
    tmp[2*len+1]='#';
    tmp[2*len+2]='$';//字符串结尾加一个字符,防止越界
    tmp[2*len+3]=0;
    return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
int manacher(char *st,int len){
     int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值
     for(int i=1;i<=len;i++){
         if(mx>i)
             Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
         else
             Len[i]=1;//如果i>=mx,要从头开始匹配
         while(st[i-Len[i]]==st[i+Len[i]])
             Len[i]++;
         if(Len[i]+i>mx){//若新计算的回文串右端点位置大于mx,要更新po和mx的值
             mx=Len[i]+i;
             po=i;
         }
         ans=max(ans,Len[i]);
     }
     return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度
}
inline void init(){
    memset(str,'\0',sizeof str);
    memset(tmp,'\0',sizeof tmp);
    memset(c,0,sizeof c);
    memset(pos,0,sizeof pos);
    for(int i=0;i<MAXN;i++)
        v[i].clear();
    res=0;
}

int main(){
    scanf("%d",&t);
    while(t--){
        init();
        scanf("%s",str);
        n=cal(str);
        manacher(tmp,n);
        int p=0;
        for(int i=2;i<n;i+=2){
            pos[++p]=Len[i]/2-1;
            v[p-pos[p]].push_back(p);
        }
        n=strlen(str);    
        for(int i=1;i<=n;i++){
            for(int j=0;j<(int)v[i].size();j++)
                update(v[i][j]);
            res+=getsum(min(i+pos[i],n))-getsum(i);
        }    
        printf("%lld\n",res);    
    }
    return 0;
}

 

posted @ 2017-12-01 23:16  勿忘初心0924  阅读(313)  评论(0编辑  收藏  举报