【题解】Palindrome pairs [Codeforces159D]

【题解】Palindrome pairs [Codeforces159D]

传送门:\(Palindrome\) \(pairs\) \([CF159D]\)

【题目描述】

给定一个长度为 \(N\) 的字符串 \(S\),求有多少四元组 \((l_1,r_1,l_2,r_2)\) 满足 \(1 \leqslant l_1 \leqslant r_1 \leqslant l_2 \leqslant r_2 \leqslant N\)\(S[l1...r1],\) \([Sl2...r2]\) 都是回文串。

【样例】

样例输入:
aa
样例输出:
1

样例输入:
aaa
样例输出:
5

样例输入:
abacaba
样例输出:
36

【数据范围】

\(100\%\) \(n \leqslant 2000\)


【分析】

由于这道题数据较小,直接写暴力也可以过(见隔壁大佬),但\(Palisection\) \([CF17E]\)就过不了了,这时候我们需要更高效的算法。

首先,用 \(Manacher\) 求出一个 \(f[i]\) 数组,用其表示以 \(a[i]\) 为中心最多可以匹配的回文串半径,那么就可以递推了。

实际上是要求互不相交的回文串对数,与\(Palisection\) \([CF17E]\)恰恰相反。
对于每一个回文串 \([l,r]\),凡是 \([1,l-1]\) 中的回文串都可以与之形成合法四元组,可以用 \(st[i],ed[i]\) 分别表示以 \(a[i]\) 开头和以 \(a[i]\) 结尾的回文串个数,那么 \(ans=\sum_{i=2}^{n}{\sum_{j=1}^{i-1}st[j]*ed[i]}\)

初始化时要对 \(st,ed\) 进行区间修改,查询时是单点查询,可以用线段树或者树状数组,不过有点麻烦,可以直接存差分数组,然后统计答案时用一个变量 \(S\) 优化掉枚举 \(st\) 求和的过程。

时间复杂度:\(O(n)\)

【Code】

#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
const int N=2003;
int n=1,m,p,q,f[N<<1];LL S,ans,st[N<<1],ed[N<<1];char a[N],b[N<<1];//记得开long long
inline int min(Re a,Re b){return a<b?a:b;}
int main(){
    scanf("%s",a+1),m=strlen(a+1);
    for(Re i=1;i<=m;++i,++n)b[++n]=a[i];//玄学填空法
    b[0]=1,b[n+1]=2;//放置首尾两边多余部分被匹配
    for(Re i=1;i<=n;++i){//Manacher
    	f[i]=q>i?min(f[(p<<1)-i],q-i):1;
    	while(b[i-f[i]]==b[i+f[i]])++f[i];
    	if(i+f[i]>q)q=(p=i)+f[i];
    }
    for(Re i=1;i<=n;++i){
    	++st[i-f[i]+1],--st[i+1];//区间修改[i-f[i]+1,i]
    	++ed[i],--ed[i+f[i]-1+1];//区间修改[i,i+f[i]-1]
    }
    for(Re i=1;i<=n;++i){
    	st[i]+=st[i-1],ed[i]+=ed[i-1];//差分求和算出单个的st,ed
    	if(!(i%2))ans+=S*st[i],S+=ed[i];
    }
    printf("%lld",ans);
}
posted @ 2019-08-22 08:30  辰星凌  阅读(342)  评论(0编辑  收藏  举报