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);

    }

}

posted @ 2016-08-07 11:44  幻世沉溺  阅读(197)  评论(0编辑  收藏  举报