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)求解