標題:切開字符串
Pear有一個字符串,不過他希望把它切成兩段。
這是一個長度爲N(<=10^5)的字符串。
Pear希望選擇一個位置,把字符串不重複不遺漏地切成兩段,長度分別是t和N-t(這兩段都必須非空)。
Pear用如下方式評估切割的方案:
定義“正迴文子串”爲:長度爲奇數的迴文子串。
設切成的兩段字符串中,前一段中有A個不相同的正迴文子串,後一段中有B個不相同的非正迴文子串,則該方案的得分爲A*B。
注意,後一段中的B表示的是:“...非正迴文...”,而不是: “...正迴文...”。
那麼所有的切割方案中,A*B的最大值是多少呢?
【輸入數據】
輸入第一行一個正整數N(<=10^5)
接下來一行一個字符串,長度爲N。該字符串僅包含小寫英文字母。
【輸出數據】
一行一個正整數,表示所求的A*B的最大值。
【樣例輸入】
10
bbaaabcaba
【樣例輸出】
38
【數據範圍】
對於20%的數據,N<=100
對於40%的數據,N<=1000
對於100%的數據,N<=10^5
題比較難,有一定思維難度,而且代碼實現複雜。首先,我們肯定要求出每個位置爲中心的迴文半徑,這個可以用Manacher算法或Hash。接下來,我們需要求出每個前綴有多少不同的迴文串(這裏提到的所有迴文串都是“正迴文串”即長度爲奇數),然後求出後綴有多少不同的迴文串,和後綴有多少不同的子串。對於迴文串的問題,由Manacher算法的引理可知,不同的迴文串個數是O(N)的,因此我們可以暴力找出所有迴文串,用一個set維護Hash值。接下來,求出這些子串在原串中第一次和最後一次出現的位置,就可以知道前綴和後綴分別有多少不同的迴文串。這一步可以先對原串構建後綴數組,然後在後綴數組中這個子串對應了連續的一段,求區間RMQ即可得到第一次、最後一次出現位置。然後處理“後綴的不同子串”數量。我們知道,如果是求一個完整字符串的不同子串,則構建後綴數組後,總子串數-Σ(相鄰兩項的LCP)就是答案。因此這兒我們考慮從後往前逐個插入後綴,動態維護當前答案——用一個平衡樹或者set維護目前爲止所有後綴的序列,每當插入一個字符串時,只有O(1)個相鄰的LCP被改動,可以通過lower_bound找出前驅後繼然後維護有關操作。整個算法複雜度爲O(Nlog^2N),瓶頸在查找“後綴數組中連續的一段”那邊。對於20%的數據是完全暴力,對於40%的數據也不難設計迴文半徑、不同子串的O(N^2)算法。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <set>
#include <algorithm>
#include <map>
#include <bitset>
#include <vector>
#include <queue>
#include <stack>
#include <utility>
#include <functional>
#include <sstream>
#include <list>
#include <complex>
#include <ctime>
#define maxlongint 2147483647
#define biglongint 2139062143
#define LL long long
#define ULL unsigned long long
#define p_b push_back
#define m_p make_pair
#define l_b lower_bound
#define w1 first
#define w2 second
using namespace std;
typedef pair<int,int> PII;
typedef pair<pair<int,int>,int> PIII;
typedef pair<pair<int,int>,pair<int,int> > PIIII;
typedef pair<double,double> PDD;
typedef pair<double,int> PDI;
typedef pair<string,int> PSI;
typedef pair<pair<double,double>,double> PDDD;
typedef pair<pair<double,double>,pair<double,double> > PDDDD;
const int maxn=100005;
const int base=987654323;
int N,o;
char st[maxn];
int a[maxn],b[maxn];
int mark[maxn],temp[maxn],c[maxn],d[maxn],bb[maxn],c1[maxn],d1[maxn],over[maxn],biao[maxn],t[maxn];
int which[maxn],height[maxn],kuo[maxn],lou2[maxn],rank[maxn],sp[maxn],fp[maxn];
int r1[maxn][18],r2[maxn][18],r3[maxn][18];
PII ls[maxn],tq[maxn*10];
int e,p,u,v,tc,x1,x2,xc,yc,mj,ll,rr,xx,yy;
ULL hash1[maxn],hash2[maxn],powc[maxn],cc;
set<ULL> hwc;
set<ULL>::iterator ltk;
set<int> f;
set<int>::iterator jcb;
bool equal(int a,int b,int c)
{
ULL k1=hash1[a+c-1]-hash1[a-1]*powc[c];
ULL k2=hash2[b+c-1]-hash2[b-1]*powc[c];
if (k1==k2) return true; else return false;
}
bool equal_2(int a,int b,int c)
{
if ((a+c-1>N)||(b+c-1>N)) return false;
ULL k1=hash1[a+c-1]-hash1[a-1]*powc[c];
ULL k2=hash1[b+c-1]-hash1[b-1]*powc[c];
if (k1==k2) return true; else return false;
}
int getmin_r1(int l,int r)
{
int cc=lou2[r-l+1];
return min(r1[l][cc],r1[r-(1<<cc)+1][cc]);
}
int getmin_r2(int l,int r)
{
int cc=lou2[r-l+1];
return min(r2[l][cc],r2[r-(1<<cc)+1][cc]);
}
int getmax_r3(int l,int r)
{
int cc=lou2[r-l+1];
return max(r3[l][cc],r3[r-(1<<cc)+1][cc]);
}
int main()
{
scanf("%d",&N);
scanf("%s",st);
for (int i=1;i<=N;i++)
{
a[i]=st[i-1]-96;
b[i]=st[N-i]-96;
}
// suffix array construction
for (int i=1;i<=N;i++)
temp[a[i]]=1;
for (int i=1;i<=255;i++)
if (temp[i]==1)
temp[i]=temp[i-1]+1;
else
temp[i]=temp[i-1];
for (int i=1;i<=N;i++) mark[i]=temp[a[i]];
for (int i=1;i<=N;i++) c[i]=i;
for (int i=1;i<=N;i++) ls[i]=m_p(mark[i],c[i]);
sort(ls+1,ls+N+1);
for (int i=1;i<=N;i++)
mark[i]=ls[i].w1,c[i]=ls[i].w2;
for (int i=1;i<=N;i++) d[c[i]]=i;
e=1;
while (e<=N)
{
memset(biao,0,sizeof(biao));
over[N]=N;
for (int i=N-1;i>=1;i--)
if (mark[i]!=mark[i+1]) over[i]=i; else over[i]=over[i+1];
for (int i=N;i>=1;i--)
{
p=c[i];
if (p<=e) continue;
p=d[p-e];
u=over[p];
v=u-biao[u];
c1[v]=c[i]-e;
d1[c[i]-e]=v;
bb[v]=mark[i];
biao[u]++;
}
for (int i=N+1;i<=N+e;i++)
{
p=d[i-e];
u=over[p];
v=u-biao[u];
c1[v]=c[p];
d1[c[p]]=v;
bb[v]=0;
biao[u]++;
}
for (int i=1;i<=N;i++) c[i]=c1[i],d[i]=d1[i];
t[0]=0;
for (int i=1;i<=N;i++)
if ((mark[i]==mark[i-1])&&(bb[i]==bb[i-1]))
t[i]=t[i-1];
else
t[i]=t[i-1]+1;
for (int i=1;i<=N;i++) mark[i]=t[i];
if (mark[N]==N) break;
e*=2;
}
for (int i=1;i<=N;i++) which[mark[d[i]]]=i;
for (int i=1;i<=N;i++) rank[which[i]]=i;
/*for (int i=1;i<=N;i++)
{
for (int j=which[i];j<=N;j++) cout<<st[j-1];
cout<<endl;
}*/
height[0]=1;
for (int i=1;i<=N;i++)
{
tc=d[i];
height[tc]=height[d[i-1]]-1;
if (height[tc]<0) height[tc]=0;
x1=i,x2=which[d[i]-1];
while (a[x1+height[tc]]==a[x2+height[tc]]) ++height[tc];
}
//for (int i=1;i<=N;i++) cout<<height[i]<<endl;
//hash array construction
hash1[0]=0;
for (int i=1;i<=N;i++) hash1[i]=hash1[i-1]*base+a[i];
hash2[0]=0;
for (int i=1;i<=N;i++) hash2[i]=hash2[i-1]*base+b[i];
powc[0]=1;
for (int i=1;i<=N;i++) powc[i]=powc[i-1]*base;
//Manacher-in-hash
int left,right,mid;
for (int i=1;i<=N;i++)
{
left=0;right=min(i-1,N-i);
while (left<=right)
{
mid=(left+right)/2;
if (equal(i-mid,N+1-i-mid,mid*2+1)) left=mid+1; else right=mid-1;
}
kuo[i]=right;
}
//for (int i=1;i<=N;i++) cout<<kuo[i]<<endl;
//two rmq constructions
for (int i=N;i>=1;i--)
{
r1[i][0]=which[i];
r2[i][0]=height[i];
r3[i][0]=which[i];
for (int j=1;j<=17;j++)
r1[i][j]=min(r1[i][j-1],r1[i+(1<<(j-1))][j-1]),
r2[i][j]=min(r2[i][j-1],r2[i+(1<<(j-1))][j-1]),
r3[i][j]=max(r3[i][j-1],r3[i+(1<<(j-1))][j-1]);
}
lou2[1]=0;
for (int i=2;i<=N;i++) lou2[i]=lou2[i/2]+1;
o=0;
for (int i=1;i<=N;i++)
{
for (int j=kuo[i];j>=0;j--)
{
xc=i-j;
yc=i+j;
cc=hash1[yc]-hash1[xc-1]*powc[yc-xc+1];
ltk=hwc.l_b(cc);
if ((*ltk)==cc) break;
++o,tq[o]=m_p(xc,yc);
hwc.insert(cc);
}
}
//first part
memset(fp,0,sizeof(fp));
memset(sp,0,sizeof(sp));
for (int i=1;i<=o;i++)
{
xc=tq[i].w1,yc=tq[i].w2;
mj=rank[xc];
int left=1,right=mj,mid;
while (left<=right)
{
mid=(left+right)/2;
if (equal_2(which[mid],xc,yc-xc+1)) right=mid-1; else left=mid+1;
}
ll=left;
left=mj,right=N;
while (left<=right)
{
mid=(left+right)/2;
if (equal_2(which[mid],xc,yc-xc+1)) left=mid+1; else right=mid-1;
}
rr=right;
fp[getmin_r1(ll,rr)+(yc-xc)]++;
sp[getmax_r3(ll,rr)]++;
}
for (int i=1;i<=N;i++) fp[i]=fp[i-1]+fp[i];
for (int i=N;i>=1;i--) sp[i]=sp[i+1]+sp[i];
LL ans=0,tdk=0,Q;
//second part
for (int i=N;i>=2;i--)
{
f.insert(rank[i]);
jcb=f.l_b(rank[i]);
if (jcb==f.begin()) xx=-1; else --jcb,xx=(*jcb),++jcb;
++jcb;
if (jcb==f.end()) yy=-1; else yy=(*jcb);
--jcb;
if ((xx!=-1)&&(yy!=-1))
tdk=tdk-getmin_r2(xx+1,yy)+getmin_r2(xx+1,rank[i])+getmin_r2(rank[i]+1,yy);
else
{
if (yy!=-1)
tdk+=getmin_r2(rank[i]+1,yy);
else
tdk+=getmin_r2(xx+1,rank[i]);
}
//cout<<tdk<<" "<<rank[i]<<endl;
Q=(LL)(N-i+1)*(N-i+2)/2;
ans=max(ans,(LL)fp[i-1]*(Q-tdk-sp[i]));
//cout<<i<<" "<<fp[i-1]<<" "<<Q-tdk-sp[i]<<endl;
}
cout<<ans<<endl;
return 0;
}