后缀数组小结

Posted moyiii

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后缀数组小结相关的知识,希望对你有一定的参考价值。

       后缀数组是从去成都之前开始学的,但是当时只是写了遍板子既不会背也不会用。从成都回来之后依然不想做这个专题,到一月中旬才填坑。刚开始因为板子不理解非常费力气,后来慢慢写着写着就会背了,背着背着也有一种理解了一点的错觉……可能并不是十分理解原理,只不过学会了怎么用而已。按自己原来的题解和日记整理一下。

       入门时看的资料:五分钟并不能学会后缀数组用法总结罗穗骞的论文。板子是从Narcissus那学的,他的总结也十分详细,给了我很大帮助。

 

       1.板子与套路

 1 void rsort()
 2 {
 3     memset(tx,0,sizeof(int)*(m+1));
 4     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
 5     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
 6     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
 7 }
 8 void suffix()
 9 {
10     int i,j,k,l,p;
11     for(i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
12     m=inf,rsort();
13     for(l=1,p=1;p<n;m=p,l<<=1)
14     {
15         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
16         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
17         rsort(),swap(rk,ty),rk[sa[1]]=p=1;
18         for(i=2;i<=n;i++)  rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
19     }
20     for(i=1,k=0;i<=n;h[rk[i++]]=k)
21         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
22 }

        $rsort$部分是一个基数排序。这是一个倍增的过程,先求出$sa$和$rk$,再根据性质快速求出$h$(即上列讲解中的$height$)。$sa[i]$的含义是排名为$i$的后缀开头的位置,$rk[i]$的含义是以$i$位置开头的后缀的排名,$h[i]$的含义是排名为$i$和$i-1$的后缀的$lcp$(最长公共前缀);$tx$和$ty$是用于实现基数排序的桶,$n$是字符串长度而$m$是字符集大小。最常见的问题是快速求两个后缀的$lcp$,答案是它们$rk$之间$h$的最小值。如果需要解决多串问题,常把多个串拼成一个并用分隔符隔断。比较裸的题有BZOJ1717BZOJ4698

技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int sj=20010;
 7 int n,k,le,ri,mid,mx,pos,a[sj],rk[sj],sa[sj],m,cnt,tx[sj],ty[sj],h[sj];
 8 struct ls
 9 {
10     int num,vl;
11 }s[sj];
12 int comp(const ls&x,const ls&y)
13 {
14     return x.vl<y.vl;
15 }
16 bool check(int x)
17 {
18     for(int i=1;i<=n;i=pos+1)
19     {
20         pos=i;
21         while(h[pos]>=x)  pos++;
22         if(pos-i>=k-1)  return 1;
23         if(pos!=i)  pos--;
24     }
25     return 0;
26 }
27 void rsort()
28 {
29     memset(tx,0,sizeof(int)*(m+1));
30     for(int i=1;i<=n;++i)  tx[rk[ty[i]]]++;
31     for(int i=1;i<=m;++i)  tx[i]+=tx[i-1];
32     for(int i=n;i>=1;--i)  sa[tx[rk[ty[i]]]--]=ty[i];
33 }
34 void suffix()
35 {
36     int i,j,k,p,l;
37     for(int i=1;i<=n;++i)  rk[i]=a[i],ty[i]=i;
38     m=sj-10;rsort();
39     for(p=1,l=1;p<n;m=p,l<<=1)
40     {
41         for(p=0,i=n-l+1;i<=n;++i)  ty[++p]=i;
42         for(i=1;i<=n;++i)  if(sa[i]>l)  ty[++p]=sa[i]-l;
43         rsort(),swap(rk,ty),rk[sa[1]]=p=1;
44         for(i=2;i<=n;++i)  rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
45     }
46     for(i=1,k=0;i<=n;h[rk[i++]]=k)  
47         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];++k);
48 }
49 int main()
50 {
51     scanf("%d%d",&n,&k);
52     for(int i=1;i<=n;i++)  scanf("%d",&a[i]),s[i].vl=a[i],s[i].num=i;
53     sort(s+1,s+n+1,comp);
54     a[s[1].num]=1,mx=1;
55     for(int i=2;i<=n;i++)
56     {
57         if(s[i-1].vl!=s[i].vl)  mx++;
58         a[s[i].num]=mx;
59     }
60     le=1,ri=n;
61     suffix();
62     while(le<ri)
63     {
64         mid=(le+ri+1)>>1;
65         if(check(mid))  le=mid;
66         else  ri=mid-1;
67     }
68     printf("%d",le);
69     return 0;
70 }
1717
技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int sj=1000010;
 6 const int bd=2000;
 7 int n,m,h[sj],tx[sj],ty[sj],rk[sj],sa[sj],a[sj];
 8 int le,ri,mid,num,bl[sj],pos,la,nt,cnt;
 9 bool r[sj];
10 void rsort()
11 {
12     memset(tx,0,sizeof(int)*(m+1));
13     for(int i=1;i<=cnt;++i)  tx[rk[ty[i]]]++;
14     for(int i=1;i<=m;++i)    tx[i]+=tx[i-1];
15     for(int i=cnt;i>=1;--i)  sa[tx[rk[ty[i]]]--]=ty[i];
16 }
17 void getsa()
18 {
19     int i,j,p,l,k;
20     for(i=1;i<=cnt;++i)  rk[i]=a[i],ty[i]=i;
21     m=4000,rsort();
22     for(p=1,l=1;p<cnt;m=p,l<<=1)
23     {
24         for(p=0,i=cnt-l+1;i<=cnt;++i)  ty[++p]=i;
25         for(i=0;i<=cnt;++i)  if(sa[i]>l)  ty[++p]=sa[i]-l;
26         rsort(),swap(rk,ty),rk[sa[1]]=p=1;
27         for(i=2;i<=cnt;++i)  
28             rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
29     }
30     for(i=1,k=0;i<=cnt;h[rk[i++]]=k)
31         for(k=k?k-1:0,j=sa[rk[i]-1];a[i+k]==a[j+k];++k);
32 }
33 bool check(int x)
34 {
35     for(int i=1;i<=cnt;i=pos+1)
36     {
37         pos=i,num=0;
38         while(h[pos]>=x) 
39         {
40            if(bl[sa[pos]]&&!r[bl[sa[pos]]])  num++;
41            r[bl[sa[pos]]]=1;
42            pos++;
43         }
44         if(h[i]>=x)  
45             if(bl[sa[i-1]]&&!r[bl[sa[i-1]]])  num++;
46         for(int j=i;j<pos;++j)  r[bl[sa[j]]]=0;
47         if(num==n)  return 1;
48     }
49     return 0;
50 }
51 int main()
52 {
53     scanf("%d",&n);
54     ri=0x7fffffff;
55     for(int i=1;i<=n;++i)
56     {
57         scanf("%d",&m);
58         if(m<ri)  ri=m;
59         for(int j=1;j<=m;++j)
60         {
61             scanf("%d",&nt);
62             a[++cnt]=nt-la+bd,la=nt;
63             bl[cnt]=i;
64         }
65         ++cnt,la=0;
66     }
67     ri--;
68     getsa();
69     while(le<ri)
70     {
71         mid=(le+ri+1)>>1;
72         if(check(mid))  le=mid;
73         else  ri=mid-1;
74     }
75     if(n==1)  printf("0");
76     else      printf("%d",le+1);
77     return 0;
78 }
4698

 

       2.BZOJ3238 差异

      观察题目中给的式子,每个后缀的长度都会被加$n-1$次,后缀的长度又是一个公差为$1$的等差数列,可以直接用求和公式$n*(n+1)/2*(n-1)$完成统计。剩下的就是后缀之间两两$lcp$的长度和,可以转化为每个长度*$lcp$等于该长度的后缀数。$lcp$是$rk$区间$h$的最小值,可以用单调栈求出每个$h$控制的范围,就变成暑假集训常做的那种NOIP模拟题了。

技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<stack>
 5 #define ll long long
 6 using namespace std;
 7 const int sj=500010;
 8 stack<int> q;
 9 int tx[sj],ty[sj],rk[sj],sa[sj],m,h[sj],a[sj],lm[sj],rm[sj];
10 char s[sj];
11 ll ans,n;
12 void rsort()
13 {
14     memset(tx,0,sizeof(int)*(m+1));
15     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
16     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
17     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
18 }
19 void suffix()
20 {
21     int i,j,k,l,p;
22     for(i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
23     m=26;rsort();
24     for(l=1,p=1;p<n;m=p,l<<=1)
25     {
26         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
27         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
28         rsort(),swap(ty,rk),rk[sa[1]]=p=1;
29         for(i=2;i<=n;i++)
30             rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
31     }
32     for(i=1,k=0;i<=n;h[rk[i++]]=k)
33         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
34 }
35 int main()
36 {
37     scanf("%s",s);
38     n=strlen(s);
39     ans=n*(n+1)/2*(n-1);
40     for(int i=1;i<=n;i++)  a[i]=s[i-1]-a+1;
41     suffix();
42     q.push(0),h[0]=-1;
43     for(int i=1;i<=n;i++)
44     {
45         while(h[q.top()]>=h[i])  q.pop();
46         lm[i]=q.top()+1;
47         q.push(i);
48     }
49     while(!q.empty())  q.pop();
50     q.push(n+1),h[n+1]=-1;
51     for(int i=n;i>=1;i--)
52     {
53         while(h[q.top()]>h[i])  q.pop();
54         rm[i]=q.top()-1;
55         q.push(i);
56     }
57     for(ll i=1;i<=n;i++)  
58         ans-=(i-lm[i]+1)*(rm[i]-i+1)*2*h[i];
59     printf("%lld",ans);
60     return 0;
61 }
3238

 

 

       3.BZOJ4556 字符串

        求一个字符串内一个子串和另一个子串的子串们的$lcp$。$lcp$的长度受到h和两个子串长度的双重限制,于是考虑二分答案来确定可行区间的端点。例如我们要求$[a,b]$的子串和$[c,d]$的$lcp$,二分$lcp$长度为$mid$,那么只能是开头在$[a,b-mid+1]$的后缀和$[c,d]$的$lcp$。维护一个下标为字符串中对应位置、权值为$rk$的线段树,每次$check$利用$h$数组二分两次求出$rk$在$[l,r]$的区间满足要求,再在主席树上查询$[a,b-mid+1]$中是否有rk落在$[l,r]$中。总复杂度$mlog^2n$。

技术分享图片
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 using namespace std;
  5 const int sj=100010;
  6 int rk[sj],tx[sj],ty[sj],sa[sj],h[sj],a[sj],p[sj][20];
  7 int rt[sj],n,q,a1,a2,a3,a4,l,r,mid,lm,rm,cnt,m;
  8 char s[sj];
  9 struct tree
 10 {
 11     int sum,lc,rc;
 12 }t[sj*50];
 13 inline int read()
 14 {
 15     int jg=0,jk=getchar()-0;
 16     while(jk<0||jk>9)    jk=getchar()-0;
 17     while(jk>=0&&jk<=9)  jg*=10,jg+=jk,jk=getchar()-0;
 18     return jg;
 19 }
 20 inline int bj(int x,int y)
 21 {
 22     return x<y?x:y;
 23 }
 24 void pre()
 25 {
 26     for(int i=1;i<=n;i++)  p[i][0]=h[i];
 27     for(int j=1;(1<<j)<=n;j++)
 28         for(int i=1;i+(1<<j)-1<=n;i++)
 29             p[i][j]=bj(p[i][j-1],p[i+(1<<(j-1))][j-1]);
 30 }
 31 void rsort()
 32 {
 33     memset(tx,0,sizeof(int)*(m+1));
 34     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
 35     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
 36     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
 37 }
 38 void suffix()
 39 {
 40     int i,j,k,p,l;
 41     for(int i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
 42     m=26;rsort(); 
 43     for(p=1,l=1;p<n;m=p,l<<=1)
 44     {
 45         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
 46         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
 47         rsort(),swap(rk,ty),rk[sa[1]]=p=1;
 48         for(i=2;i<=n;i++)  rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
 49     } 
 50     for(i=1,k=0;i<=n;h[rk[i++]]=k)
 51         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];++k);
 52 }
 53 void insert(int nt,int la,int z,int y,int vl)
 54 {
 55     t[nt].sum=t[la].sum+1,t[nt].lc=t[la].lc,t[nt].rc=t[la].rc;
 56     if(z==y)  return;
 57     int mi=z+y>>1;
 58     if(vl<=mi)  t[nt].lc=++cnt,insert(t[nt].lc,t[la].lc,z,mi,vl);
 59     else        t[nt].rc=++cnt,insert(t[nt].rc,t[la].rc,mi+1,y,vl);   
 60 }
 61 bool query(int nt,int la,int z,int y,int le,int ri)
 62 {
 63     if(t[la].sum-t[nt].sum==0)  return 0;
 64     if(z==le&&y==ri)            return 1;
 65     int mi=z+y>>1;
 66     if(ri<=mi)  return query(t[nt].lc,t[la].lc,z,mi,le,ri);
 67     if(le>mi)   return query(t[nt].rc,t[la].rc,mi+1,y,le,ri);
 68     bool p1=query(t[nt].lc,t[la].lc,z,mi,le,mi);
 69     bool p2=query(t[nt].rc,t[la].rc,mi+1,y,mi+1,ri);
 70     return (p1|p2);
 71 }
 72 int main()
 73 {
 74     n=read(),q=read();
 75     scanf("%s",s);
 76     for(int i=1;i<=n;i++)  a[i]=s[i-1]-a+1;
 77     suffix();pre();
 78     for(int i=1;i<=n;i++)
 79     {
 80         rt[i]=++cnt;
 81         insert(rt[i],rt[i-1],1,n,rk[i]);
 82     }
 83     for(int i=1;i<=q;i++)
 84     {
 85         a1=read(),a2=read(),a3=read(),a4=read();   
 86         l=0,r=bj(a4-a3+1,a2-a1+1);
 87         while(l<r)
 88         {
 89             mid=(l+r+1)>>1,lm=rm=rk[a3];
 90             for(int j=16;j>=0;j--) 
 91                 if(lm>(1<<j)&&p[lm-(1<<j)+1][j]>=mid)
 92                     lm-=(1<<j);
 93             for(int j=16;j>=0;j--)
 94                 if(rm+(1<<j)<=n&&p[rm+1][j]>=mid)
 95                     rm+=(1<<j);
 96             if(query(rt[a1-1],rt[a2-mid+1],1,n,lm,rm))  l=mid;
 97             else  r=mid-1;
 98         }
 99         printf("%d\n",l);
100     }
101     return 0;
102 }
4556

 

       4.BZOJ2754 喵星球上的点名

        暴力水过去的,并不知道正解应该怎么写。把所有姓名和询问都接成一个串,对每一个询问RMQ求出可行区间,然后就暴力扫可行区间统计答案。实际上复杂度非常没保障。 COGS上数据是学长强化过的,貌似只有cooook过了。

技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 inline int read()
 6 {
 7     int jg=0,jk=getchar()-0;
 8     while(jk<0||jk>9)    jk=getchar()-0;
 9     while(jk>=0&&jk<=9)  jg*=10,jg+=jk,jk=getchar()-0;
10     return jg;
11 }
12 const int sj=300010;
13 const int inf=10001;
14 int h[sj],rk[sj],sa[sj],tx[sj],ty[sj],cnt,q,m,a[sj],n,len,a1,bl[sj],p[sj][20];
15 int st[50010],ln[50010],num[20010],ans,lm,rm;
16 bool r[20010];
17 inline int bj(int x,int y)
18 {
19     return x<y?x:y;
20 }
21 void pre()
22 {
23     for(int i=1;i<=n;i++)  p[i][0]=h[i];
24     for(int j=1;(1<<j)<=n;j++)
25         for(int i=1;i+(1<<j)-1<=n;i++)
26             p[i][j]=bj(p[i][j-1],p[i+(1<<(j-1))][j-1]);
27 }
28 void rsort()
29 {
30     memset(tx,0,sizeof(int)*(m+1));
31     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
32     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
33     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
34 }
35 void suffix()
36 {
37     int i,j,k,l,p;
38     for(i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
39     m=inf,rsort();
40     for(l=1,p=1;p<n;m=p,l<<=1)
41     {
42         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
43         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
44         rsort(),swap(rk,ty),rk[sa[1]]=p=1;
45         for(i=2;i<=n;i++)  rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
46     }
47     for(i=1,k=0;i<=n;h[rk[i++]]=k)
48         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
49 }
50 int main()
51 {
52     cnt=read(),q=read();
53     for(int i=1;i<=cnt;i++)
54     {
55         len=read();
56         for(int j=1;j<=len;j++)
57             a1=read(),a[++n]=a1,bl[n]=i;
58         a[++n]=inf;
59         len=read();
60         for(int j=1;j<=len;j++)
61             a1=read(),a[++n]=a1,bl[n]=i;
62         a[++n]=inf;
63     }
64     for(int i=1;i<=q;i++)
65     {
66         ln[i]=read(),st[i]=n+1;
67         for(int j=1;j<=ln[i];j++)
68             a1=read(),a[++n]=a1;
69         a[++n]=inf; 
70     }
71     suffix();pre();
72     for(int i=1;i<=q;i++)
73     {
74         lm=rm=rk[st[i]],ans=0;
75         for(int j=19;j>=0;j--)
76             if(lm>(1<<j)&&p[lm-(1<<j)+1][j]>=ln[i])
77                 lm-=(1<<j);
78         for(int j=19;j>=0;j--)
79             if(rm+(1<<j)<=n&&p[rm+1][j]>=ln[i])
80                 rm+=(1<<j);
81         for(int j=lm;j<=rm;j++)
82             if(bl[sa[j]]&&!r[bl[sa[j]]])
83                 r[bl[sa[j]]]=1,num[bl[sa[j]]]++,ans++;
84         for(int j=lm;j<=rm;j++)
85             if(bl[sa[j]])
86                 r[bl[sa[j]]]=0;
87         printf("%d\n",ans);
88     }
89     for(int i=1;i<cnt;i++)
90         printf("%d ",num[i]);
91     printf("%d",num[cnt]);
92     return 0;
93 }
2754

 

       5.BZOJ3230 相似子串

      我居然没有在BZOJ上交这题?回校再写吧,当初好像还在代码后面附了题解的。

 

       6.BZOJ4199 品酒大会

        充分利用height数组的性质。在n相似的时候没有任何两杯酒满足要求,而0相似的时候任意两杯酒都满足要求;满足要求的总是$rk$在一个区间里的所有酒,两问的答案都可以通过区间计算得到。如果我们从大到小处理,相当于一个区间合并、更新答案的过程。注意有负权存在。刚开始犯蠢写了个主席树,T了之后才发现这完全是并查集就能解决的问题……

技术分享图片
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #define ll long long
  6 using namespace std;
  7 const int sj=300010;
  8 int n,m,a[sj],tx[sj],ty[sj],rk[sj],sa[sj],h[sj],a1,a2,fa[sj];
  9 ll vl[sj],ans1[sj],ans2[sj],sum,ans,tp1,tp2,tp,tp3,tp4,qwq,mx[sj],mi[sj],num[sj];
 10 char s[sj];
 11 struct ls
 12 {
 13     int num,val;
 14 }ss[sj];
 15 struct tree
 16 {
 17     int lc,rc,sum;
 18 }t[sj*30];
 19 int comp(const ls&x,const ls&y)
 20 {
 21     return x.val<y.val;
 22 }
 23 void rsort()
 24 {
 25     memset(tx,0,sizeof(int)*(m+1));
 26     for(int i=1;i<=n;i++)  tx[rk[ty[i]]]++;
 27     for(int i=1;i<=m;i++)  tx[i]+=tx[i-1];
 28     for(int i=n;i>=1;i--)  sa[tx[rk[ty[i]]]--]=ty[i];
 29 }
 30 void suffix()
 31 {
 32     int i,j,k,l,p;
 33     for(i=1;i<=n;i++)  rk[i]=a[i],ty[i]=i;
 34     m=26;rsort();
 35     for(p=1,l=1;p<n;m=p,l<<=1)
 36     {
 37         for(p=0,i=n-l+1;i<=n;i++)  ty[++p]=i;
 38         for(i=1;i<=n;i++)  if(sa[i]>l)  ty[++p]=sa[i]-l;
 39         rsort(),swap(ty,rk),rk[sa[1]]=p=1;
 40         for(i=2;i<=n;i++) 
 41             rk[sa[i]]=(ty[sa[i]]==ty[sa[i-1]]&&ty[sa[i]+l]==ty[sa[i-1]+l])?p:++p;
 42     }
 43     for(i=1,k=0;i<=n;h[rk[i++]]=k)
 44         for(k=k?k-1:k,j=sa[rk[i]-1];a[i+k]==a[j+k];k++);
 45 } 
 46 void init()
 47 {
 48     scanf("%d%s",&n,s);
 49     memset(fa,-1,sizeof(fa));
 50     tp1=-1ll<<62,tp2=-1ll<<62,tp3=1ll<<62,tp4=1ll<<62;
 51     for(int i=1;i<=n;i++)  
 52     {    
 53         scanf("%lld",&vl[i]);
 54         a[i]=s[i-1]-a+1;
 55         if(vl[i]>=tp1)  tp2=tp1,tp1=vl[i];  
 56         else if(vl[i]>tp2)  tp2=vl[i];
 57         if(vl[i]<=tp3)  tp4=tp3,tp3=vl[i];
 58         else if(vl[i]<tp4)  tp4=vl[i];  
 59     }
 60 }
 61 int find(int x)
 62 {
 63     if(fa[x]==-1)  return x;
 64     return fa[x]=find(fa[x]);
 65 }
 66 inline ll maxx(ll x,ll y)
 67 {
 68     return x>y?x:y;
 69 }
 70 inline ll minn(ll x,ll y)
 71 {
 72     return x<y?x:y;
 73 }
 74 int main()
 75 {
 76     init();
 77     suffix();
 78     for(int i=1;i<=n;i++)
 79     {
 80         ss[i].num=i,ss[i].val=h[i];
 81         num[i]=1,mx[i]=mi[i]=vl[sa[i]];
 82     }
 83     sort(ss+1,ss+n+1,comp);
 84     ans=-1ll<<62;
 85     for(int i=n;i>=1;i--)
 86     {
 87         if(ss[i].val!=ss[i+1].val)  
 88             ans1[ss[i+1].val]=sum/2,ans2[ss[i+1].val]=ans;
 89         a1=ss[i].num,a2=a1-1;
 90         if(!h[a1])  break;
 91         a1=find(a1),a2=find(a2);
 92         sum-=(ll)num[a1]*(num[a1]-1);
 93         sum-=(ll)num[a2]*(num[a2]-1);
 94         tp=mx[a1]*mx[a2],qwq=mi[a1]*mi[a2];
 95         if(tp>ans)   ans=tp;
 96         if(qwq>ans)  ans=qwq;
 97         if(num[a1]<num[a2])  swap(a1,a2);
 98         mx[a1]=maxx(mx[a1],mx[a2]);
 99         mi[a1]=minn(mi[a1],mi[a2]);
100         num[a1]+=num[a2];
101         fa[a2]=a1;
102         sum+=(ll)num[a1]*(num[a1]-1);
103     }
104     ans1[0]=(ll)n*(n-1)/2,ans2[0]=maxx(tp1*tp2,tp3*tp4);
105     for(int i=0;i<n;i++)
106         printf("%lld %lld\n",ans1[i],ans2[i]);
107     return 0;
108 }
4199

 


以上是关于后缀数组小结的主要内容,如果未能解决你的问题,请参考以下文章

后缀数组小结?

后缀数组模板及应用小结 附加练习题*6

后缀数组小结

字符串小结

回文自动机做题小结

后缀自动机小结