Palindrome
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 20 Accepted Submission(s): 9
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 stringS[1..3n−2](n≥2) is one-and-half palindromic if and only if it satisfies S[i]=S[2n−i]=S[2n+i−2](1≤i≤n).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 to500000), 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
Hint
Source
2017中国大学生程序设计竞赛-哈尔滨站-重现赛(感谢哈理工)
在哈尔滨的第一场比赛真的是像发疯了一样,在水题上纠结了近四个小时……当时真菜……
现在回过头来看,这道题目还是非常的巧妙的。首先,我们要清楚的明白,这个回文的性质,例如:abcbabc,有两个对称中心'c'和'a',然后第二个对称中心的长度要恰好为两个对称中心的距离。转换成符号表示就是,i<j,j-i==len[j]且j-i<=len[i]。更确切的说就是,第二个对称中心要落在第一个对称的范围内,而且第二个对称中心的长度要恰好为两个对称中心的距离。
如果之前知道Manacher算法的话,你就会知道,这里的len数组其实可以约等于其中的rl数组(有包含关系),表示对称的长度,个人习惯是长度不把对称中心算进去。然后呢为了方便表达,j-i==len[j],可以等价于j-i<=rl[j]。现在问题就变成了在从i到i+rl[i]的区间内,找有多少个j满足j-i<=rl[j]。但是处理这个还是不太容易,所以变形一下,相当于找有多少个rl[j]-j>=-i。如果变成这样,我们就发现式子右边就有了单调性,因此我们就可以离线处理用上数据结构处理修改与查询操作。
具体来说,我们要首先计算出所有的rl[j]-j,并且进行大到小排序,然后-i从-1开始一直到-n,每次的时候首先查询i+1到i+rl[i]之间总共有多少个满足条件的j。然后当此时的rl[j]-j>=-i的时候,说明从此之后这个j就可以包含剩下所有的i了,在j这个点更新。以此类推,依次更新与查询。需要做到单点更新、区间查询,用树状数组即可。具体见代码(Manacher第一次打,调了比较久……):
#include<bits/stdc++.h>
#define N 1001000
#define LL long long
using namespace std;
struct node{int x,id;} a[N];
int n,p[N],rl[N];
char s[N];
struct BinaryIndexedTree
{
LL c[N];
inline void init()
{
memset(c,0,sizeof(c));
}
inline int lowbit(int x)
{
return x&-x;
}
inline void update(int x,int k)
{
while (x<=n*2)
{
c[x]+=k;
x+=lowbit(x);
}
}
inline LL getsum(int x)
{
LL ans=0;
while (x>0)
{
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
} BIT;
void manacher()
{
int len=strlen(s);
for(int i=len;i>=0;--i)
s[i+i+1]=s[i],s[i+i]='#';
int id,mx=0;
for(int i=1;i<len+len+1;++i)
{
if (mx>i) p[i]=min(p[2*id-i],mx-i); else p[i]=1;
while(s[i-p[i]]==s[i+p[i]]&&i-p[i]>=0&&i+p[i]<len+len+1)++p[i];
if(i+p[i]>mx) id=i,mx=p[i]+i;
if (s[i]!='#') rl[(i+1)/2]=(p[i]-1)/2;
}
}
bool cmp(node a,node b)
{
return a.x>b.x;
}
int main()
{
int T_T;
cin>>T_T;
while(T_T--)
{
LL ans=0;
BIT.init();
scanf("%s",s);
n=strlen(s); manacher();
for(int i=1;i<=n;i++)
{
a[i].x=rl[i]-i;
a[i].id=i;
}
int j=-1;
sort(a+1,a+1+n,cmp); //从大到小排序
for(int i=1;i<=n;i++)
{
while(a[i].x<j&&j>=-n) //当没有可以更新的点的时候,进行查询
{
ans+=BIT.getsum(-j+rl[-j])-BIT.getsum(-j); //查询区间内可行的j的数量
j--;
}
BIT.update(a[i].id,1); //更新满足条件的点
}
while(j>=-n) //处理尾巴
{
ans+=BIT.getsum(-j+rl[-j])-BIT.getsum(-j);
j--;
}
printf("%I64d\n",ans);
}
}