题意:给出一个字符串,求其 本质不同的 回文子串的个数。
如果有小伙伴WA了无数次,请尝试模拟一下aabaa这个串,答案应该是5。(本菜鸡就WA了一晚上)
题解:回文子串可以考虑先来个O(n)的马拉车预处理,这样每个回文子串长度必然是计数,那么我们可以统计本质不同的(正中间的字符+右半边串)回文子串个数。然后可以考虑用后缀自动机统计答案。这道题的关键的关键在于去重的处理。去重要求去掉:h[i]范围内已经被统计过的串。那么可以用一个变量维护 目前已经被统计过的长度。要注意到h数组和马拉车的lc数组是没什么关系的。
Code:
#include<bits/stdc++.h>
using namespace std;
#define rank rk
const int MAX = 2e5+10000;
char ch[MAX];
int cntA[MAX],cntB[MAX],A[MAX],B[MAX],tsa[MAX],rank[MAX],SA[MAX],lc[MAX],h[MAX];
int n,t;
int Cas =1;
void init(){
memset(ch,0,sizeof ch);
ch[0]='z'+1;
}
void input(){
scanf("%s",ch+1);
n = strlen(ch+1);
ch[n*2+1]='#';
for (int i=n;i>=1;i--){
ch[i*2] = ch[i];
ch[i*2-1] ='#';
}
n = n*2+1;
ch[n+1]='\0';
}
void get_SA(){
for (int i=0;i<=10000;i++) cntA[i]=0;
for (int i=1;i<=n;i++) cntA[ch[i]]++;
for (int i=1;i<=10000;i++) cntA[i]+=cntA[i-1];
for (int i=n;i>=1;i--) SA[cntA[ch[i]]--] =i;
rank[SA[1]]=1;
for (int i=2;i<=n;i++){
rank[SA[i]]=rank[SA[i-1]];
if (ch[SA[i]]!=ch[SA[i-1]]) rank[SA[i]]++;
}
for (int step = 1;rank[SA[n]]<n;step<<=1){
for (int i=0;i<=n;i++)cntA[i]=cntB[i]=0;
for (int i=1;i<=n;i++){
cntA[A[i]=rank[i]]++;
cntB[B[i]=(i+step<=n)?rank[i+step]:0]++;
}
for (int i=1;i<=n;i++) cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1];
for (int i=n;i>=1;i--) tsa[cntB[B[i]]--] =i;
for (int i=n;i>=1;i--) SA[cntA[A[tsa[i]]]--] = tsa[i];
rank[SA[1]]=1;
for (int i=2;i<=n;i++){
rank[SA[i]]=rank[SA[i-1]];
if (A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]]) rank[SA[i]]++;
}
}
}
void get_Height(){
for (int i=1,j=0;i<=n;i++){
if (j) j--;
while (ch[i+j]==ch[SA[rank[i]-1]+j])j++;
h[rank[i]]=j;
}
}
void Manacher(){
lc[1]=1;
int k=1;
for (int i=2;i<=n;i++){
// printf("%d %d\n",i,k);
int p = k+lc[k]-1;
if (i<=p){
lc[i]=min(lc[2*k-i],p-i+1);
}else{
lc[i]=1;
}
while (ch[i+lc[i]]==ch[i-lc[i]])lc[i]++;
if (i+lc[i]>k+lc[k])k=i;
}
}
void print(){
printf("%s\n",ch+1);
for (int i=1;i<=n;i++){
printf("%s %d\n",ch+SA[i],lc[SA[i]]);
}
}
void solve(){
get_SA();
get_Height();
Manacher();
print();
long long res =0;
// cout<<"1: "<<res<<endl;
int cnt=0;
for (int i=2;i<=n;i++){
cnt = min(cnt,h[i]);
res+=max(0,lc[SA[i]]-min(h[i],cnt));
// cout<<i<<" "<<res<<endl;
if (lc[SA[i]]>cnt){
cnt = lc[SA[i]];
}
}
// cout<<res/2<<endl;
printf("Case #%d: %I64d\n",Cas++,res/2);
}
int main(){
scanf("%d",&t);
while (t--){
init();
input();
solve();
}
return 0;
}