[Codeforces 17E] Palisection

Brief Intro:

求所有相交的回文子串的个数。

 

Algorithm:

求回文子串,肯定要先Manacher

我们可以发现判断相交要考虑多种情况,既有相交又有包含,难以成段计算

但两子串不相交的条件只有一个,即A子串的右边界严格小于B子串的左边界,再用总个数相减即可

 

我们用差分维护l[i],r[i]分别表示左/右边界恰好为i的个数,同时维护r[i]的前缀和,即可O(n)地更新答案

 

Code:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int MAXN=2e6+10;
const int MOD=51123987;
int len,m[2*MAXN],l[2*MAXN],r[2*MAXN],mx,mid;
string dat,s;

void init()
{
    s="!#";
    for(int i=0;i<dat.size();i++)
        s.append(dat,i,1),s.append("#");
    s.append("@");
}

int main()
{
    cin >> len >> dat;
    init();
    
    mx=0;mid=0;
    for(int i=1;i<s.size()-1;i++)
    {
        if(i<mx) m[i]=min(mx-i,m[2*mid-i]);
        else m[i]=1;
        
        while(s[i+m[i]]==s[i-m[i]]) m[i]++;
        
        if(i+m[i]>mx) mx=i+m[i],mid=i;
    }
    
    ll res=0,sum=0;
    for(int i=1;i<s.size()-1;i++)
    {
        l[i-m[i]+1]++;l[i+1]--;r[i]++;r[i+m[i]]--; //求出该点最长回文串后,这一段均为回文子串,都要将l[i]和r[i]更新
        (res+=m[i]/2)%=MOD;
    }
    
    res=res*(res-1ll)/2%MOD;
    
    for(int i=1;i<s.size()-1;i++)
    {
        l[i]+=l[i-1];r[i]+=r[i-1];
        if(i%2==0) (res-=sum*l[i]%MOD)%=MOD,(sum+=r[i])%=MOD; 
    }
    cout << (res+MOD)%MOD; //取模出现减法要先加模数防止出现负数
    
    return 0;
}

 

Review:

1、当正向求解情况太多时,考虑反向求解,再用总个数相减

      ex:求解区间相交性问题时即可算出不相交的个数

 

2、当数据更新时为区段增减,考虑使用差分法维护,在O(n)内实现

 

3、求解区间相交问题,维护每个点恰为左右边界的个数和右边界的前缀和即可O(n)求解

 

posted @ 2018-05-14 07:23  NewErA  阅读(210)  评论(0编辑  收藏  举报