求一个字符串的∑ ∑ len[i] + len[j] - 2 * lcp(i, j),其中i,j表示从i,j开始的后缀。
方法一:SA+单调栈,自行yy。
方法二:SAM构造出来,然后每个状态对答案的贡献就是:C(|right_s|,2)*(Max_s-Max_parent_s)。前面使用的变量名含义与CLJ论文里的一致。right集合的大小需要构造后缀树然后从那些叶子节点开始算。
由于可以用马拉车在O(N)时间内枚举所有的回文串,所以在parent树中倍增找到每个回文串的出现次数即可。
跑得巨慢。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<algorithm> 5 //#include<iostream> 6 using namespace std; 7 8 int n; 9 #define maxn 600011 10 char s[maxn]; 11 12 struct samnode 13 { 14 int ch[26],pre; 15 int pos; 16 samnode() {memset(ch,0,sizeof(ch)); pre=0;} 17 }; 18 int pp[maxn],val[maxn]; 19 struct SAM 20 { 21 samnode a[maxn]; 22 int root,last,size; 23 SAM() {root=1; a[1].pos=0; size=1; last=root;} 24 int idx(char c) {return c-'a';} 25 void insert(char c,int p) 26 { 27 int id=idx(c),x=++size; 28 a[x].pos=p; pp[p]=x; val[x]=1; 29 int y=last; 30 for (;y && !a[y].ch[id];y=a[y].pre) a[y].ch[id]=x; 31 last=x; 32 if (!y) a[x].pre=root; 33 else if (a[a[y].ch[id]].pos==a[y].pos+1) a[x].pre=a[y].ch[id]; 34 else 35 { 36 int z=a[y].ch[id],w=++size; 37 a[w]=a[z]; a[w].pos=a[y].pos+1; 38 a[z].pre=a[x].pre=w; 39 for (;y && a[y].ch[id]==z;y=a[y].pre) a[y].ch[id]=w; 40 } 41 } 42 }sam; 43 44 int list[maxn],fa[maxn][22]; 45 bool cmp(const int &i,const int &j) {return sam.a[i].pos>sam.a[j].pos;} 46 void build() 47 { 48 for (int i=1;i<=sam.size;i++) list[i]=i,fa[i][0]=sam.a[i].pre; 49 sort(list+1,list+1+sam.size,cmp); 50 for (int j=1;j<=20;j++) 51 for (int i=1;i<=sam.size;i++) 52 fa[i][j]=fa[fa[i][j-1]][j-1]; 53 for (int i=1;i<=sam.size;i++) 54 val[fa[list[i]][0]]+=val[list[i]]; 55 } 56 57 #define LL long long 58 LL getans(int x,int len) 59 { 60 int p=pp[x]; 61 for (int j=20;j>=0;j--) if (fa[p][j] && sam.a[fa[p][j]].pos>=len) p=fa[p][j]; 62 // cout<<x<<' '<<len<<' '<<val[p]<<endl; 63 return 1ll*len*val[p]; 64 } 65 66 int mnc[maxn]; 67 int main() 68 { 69 scanf("%s",s+1);n=strlen(s+1); 70 for (int i=1;i<=n;i++) sam.insert(s[i],i); 71 build(); 72 73 for (int i=n;i>=1;i--) 74 { 75 s[(i<<1)-1]=s[i]; 76 s[i<<1]='#'; 77 } 78 n=n+n-1; 79 // cout<<s+1<<endl; 80 int id=0;LL ans=0; 81 for (int i=1;i<=n;i++) 82 { 83 if (mnc[id]+id>i) mnc[i]=min(mnc[id*2-i],mnc[id]+id-i); 84 else 85 { 86 mnc[i]=1; 87 if (s[i]!='#') ans=max(ans,getans((i+1)>>1,1)); 88 } 89 while (i+mnc[i]<=n && i-mnc[i]>0 && s[i+mnc[i]]==s[i-mnc[i]]) 90 { 91 if (s[i+mnc[i]]!='#') ans=max(ans,getans((i+mnc[i]+1)>>1,mnc[i]+1)); 92 mnc[i]++; 93 } 94 if (mnc[id]+id<mnc[i]+i) id=i; 95 // cout<<i<<' '<<mnc[i]<<endl; 96 } 97 printf("%lld ",ans); 98 return 0; 99 }
方法三:PAM。不会。