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 is one-and-half palindromic if and only if it satisfies S.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; }
我每天都在努力,只是想证明我是认真的活着.