HDU 5785 Interesting
题目:
大概说给一个字符串,找到其所有子串[i...k]满足它是由两个回文串拼成的,求Σi*k。
分析:
用val[1][i]表示以i结尾的回文串的起始位置的和val[0][i]表示以i起始的回文串的结尾位置的和,然后就可以求出答案了. Σ(val[1][i]*val[0][i+1])就是答案.
1.假设原串为a[]={aaa},使用manacher算法将原串变为#a#a#a#,对应的p数组为{1,2,3,4,3,2,1}.p[i]/2-1的值为新串中以i为中心的回文半径.(回文串长度为奇数,即a[i]!=’#’);p[i]/2的值为新串中以i为中心的回文半径(回文串长度为偶数,即a[i]==’#’&&p[i]!=1)
2.比如当前回文串的中间位置是i(回文串是奇数的情况,偶数同理),p[i]表示Manacher求得的延伸半径。那么[i-p[i]+1, i+p[i]-1]就是回文串,故val[1][i+p[i]-1]就该加上i-p[i]+1;而[i-p[i]+2, i+p[i]-2]也是回文串,val[1][i+p[i]-2]就该加上i-p[i]+2…………总之可以知道这个就相当于,对于val[1]数组要在[i, i+p[i]-1]区间上依次加上一个末项为i-p[i]+1且公差为-1的等差数列,val[0]也是同理的这儿同样就不说了。
2.接下来的问题就是如何对对区间更新,让区间[L,R]加上一个首项k公差-1的等差数列。想到了加标记addtag表示加多少。上述问题就转化为addtag[L]+=k;addtag[L+1]+=addtag[L]-1; 考虑到多个区间修改有叠加,开addcnt表示各个点有多少个加操作。所以addtag[L+1]+=addtag[L]-addcnt[L];addcnt[L+1]+=addcnt[L];有余这样的传递会一直传递到数组尾巴,相当于更新了[L,MAXN]区间,所以要减去[R+1,MAXN]区间的(显然也是一个等差数列)。于是用subtag和subcnt表示.处理方式同addtag和addcnt
输入:
aaa abc
输出:
14 8
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int maxn=1111111;
const int mod=1000000007;
char s[maxn<<1];
int p[maxn<<1];//p[i]存放以i为中心最长的回文串长度+1
void manacher()
{
int mx=0,id;
for(int i=1;s[i];i++)
{
if(mx>i)
p[i]=min(p[2*id-1],mx-1);
else
p[i]=1;
for(;s[i+p[i]]==s[i-p[i]];++p[i]);
if(p[i]+i>mx)
{
mx=p[i]+1;
id=1;
}
}
}
char str[maxn];
long long val[2][maxn];//val[0][i]代表以i起始的回文串的结尾位置的和,val[1][i]表示以i结尾的回文串的起始位置的和
long long addtag[2][maxn],addcnt[2][maxn],subtag[2][maxn],subcnt[2][maxn];
void update(int flag,int l,int r,int k)//flag为0处理val[0],为1处理val[1];
{
addtag[flag][l]+=k;
addtag[flag][l]%=mod;
++addcnt[flag][l];
subtag[flag][r+1]+=k-r+l;
subtag[flag][r+1]%=mod;
++subcnt[flag][r];
}
void pushdown()
{
for(int i=1;str[i];i++){
if(addcnt[0][i]){
val[0][i]+=addtag[0][i];
val[0][i]%=mod;
addtag[0][i+1]+=addtag[0][i]-addcnt[0][i];
addtag[0][i+1]%=mod;
addcnt[0][i+1]+=addcnt[0][i];
}
if(subcnt[0][i]){
val[0][i]-=subtag[0][i];
val[0][i]%=mod;
subtag[0][i+1]+=subtag[0][i]-subcnt[0][i];
subtag[0][i+1]%=mod;
subcnt[0][i+1]+=subcnt[0][i];
}
if(addcnt[1][i]){
val[1][i]+=addtag[1][i];
val[1][i]%=mod;
addtag[1][i+1]+=addtag[1][i]-addcnt[1][i];
addtag[1][i+1]%=mod;
addcnt[1][i+1]+=addcnt[1][i];
}
if(subcnt[1][i]){
val[1][i]-=subtag[1][i];
val[1][i]%=mod;
subtag[1][i+1]+=subtag[1][i]-subcnt[1][i];
subtag[1][i+1]%=mod;
subcnt[1][i+1]+=subcnt[1][i];
}
}
}
int main()
{
while(~scanf("%s",str+1))
{
s[0]='$';
int i=1;
for(;str[i];i++)
{
s[(i<<1)-1]='#';
s[i<<1]=str[i];
}
s[(i<<1)-1]='#';
s[i<<1]=0;
manacher();
// for(int i=1;s[i];i++)
// cout<<p[i]<<" "<<s[i]<<endl;
memset(val,0,sizeof(val));
memset(addtag,0,sizeof(addtag));
memset(addcnt,0,sizeof(addcnt));
memset(subtag,0,sizeof(subtag));
memset(subcnt,0,sizeof(subcnt));
for(int i=1;s[i];i++)
{
if(i&1){//处理回文串长度为偶数的情况
// cout<<s[i]<<endl;
if(p[i]/2==0)
continue;
update(0, i/2-p[i]/2+1, i/2, i/2+p[i]/2);
update(1, i/2+1, i/2+p[i]/2, i/2);
}
else{//处理回文串长度为奇数的情况
update(0, i/2-p[i]/2+1, i/2, i/2+p[i]/2-1);
update(1, i/2, i/2+p[i]/2-1, i/2);
}
}
pushdown();
long long ans=0;
for(int i=1;str[i]&&str[i+1];i++){
//cout<<i<<endl;
ans+=val[1][i]*val[0][i+1];
ans%=mod;
}
if(ans<0)
ans+=mod;
printf("%lld\n",ans);
}
}