[loj6198]谢特

题目链接

loj

题解

主要是为了练练SA。。。

SAM

两个后缀的最长公共前缀是两个点的 lca 处,那么枚举这个 $lca$ 进行统计,建一个Trie存子树中的 $w_i$,每次合并时启发式合并。

SA

两个后缀的最长公共前缀是后缀数组中的一段区间的最小值。

那么从大到小枚举这个最小值的位置,还是跟SAM那样启发式合并Trie即可。

程序

SA做法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
#define CASET fo(___,1,read())

const int N=100010;

int base[N],rk[N],sa[N],t[N],height[N],f[N][18],l2[N];
inline void rsort(int n,int m)
{
fo(i,1,m) base[i]=0;
fo(i,1,n) base[rk[t[i]]]++;
fo(i,1,m) base[i]+=base[i-1];
fd(i,n,1) sa[base[rk[t[i]]]--]=t[i];
}
inline bool cmp(int x,int y,int l)
{
return (t[x]==t[y])&&(t[x+l]==t[y+l]);
}
inline void SA(char *s,int n)
{
s[n+1]=0;
int m=27;
fo(i,1,n) rk[i]=s[i]-'a'+1,t[i]=i;
rsort(n,m);
for(int w=1,p;p<n;w<<=1)
{
p=0;
fo(i,n-w+1,n) t[++p]=i;
fo(i,1,n) if(sa[i]>w) t[++p]=sa[i]-w;
rsort(n,p);
fo(i,1,n) t[i]=rk[i];
rk[sa[1]]=p=1;
fo(i,2,n) rk[sa[i]]=(cmp(sa[i],sa[i-1],w)?p:++p);
}
for(int i=1,j,k=0;i<=n;height[rk[i++]]=k)
for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
fo(i,1,n) f[i][0]=height[i];
l2[1]=0;
fo(i,2,n) l2[i]=l2[i>>1]+1;
fo(j,1,l2[n])
fo(i,1,n)
{
f[i][j]=f[i][j-1];
if(i+(1<<j-1)<=n) f[i][j]=min(f[i][j],f[i+(1<<j-1)][j-1]);
}
}
inline int ask(int x,int y)
{
x=rk[x]; y=rk[y];
if(x>y) swap(x,y);
x++; int l=l2[y-x+1];
return min(f[x][l],f[y-(1<<l)+1][l]);
}

int ne[N<<5][2],rt[N],cnt;
inline int build(int x)
{
int u=++cnt,rt=u;
fd(i,18,0)
{
ne[u][(x>>i)&1]=++cnt;
u=cnt;
}
return rt;
}
int query(int x,int y,int v)
{
//if(!ne[x][0]&&!ne[x][1]) return v;
int ans=v;
if(ne[x][0])
{
if(ne[y][1]) ans=max(ans,query(ne[x][0],ne[y][1],(v<<1)|1));
else ans=max(ans,query(ne[x][0],ne[y][0],v<<1));
}
if(ne[x][1])
{
if(ne[y][0]) ans=max(ans,query(ne[x][1],ne[y][0],(v<<1)|1));
else ans=max(ans,query(ne[x][1],ne[y][1],v<<1));
}
return ans;
}
void merge(int x,int y)
{
if(ne[x][0])
{
if(ne[y][0]) merge(ne[x][0],ne[y][0]);
else ne[y][0]=ne[x][0];
}
if(ne[x][1])
{
if(ne[y][1]) merge(ne[x][1],ne[y][1]);
else ne[y][1]=ne[x][1];
}
}

int fa[N],siz[N],id[N];

int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}

int val[N],ans,n;
char s[N];
int main()
{
n=read();
scanf("%s",s+1);
SA(s,n);
fo(i,1,n) val[i]=read();
fo(i,1,n) id[i]=i,fa[i]=i,siz[i]=1,rt[i]=build(val[sa[i]]);
sort(id+2,id+n+1,[&](const int &x,const int &y){return height[x]>height[y];});
int x,y;
fo(i,2,n)
{
x=id[i]; y=x-1;
x=find(x); y=find(y);
if(siz[x]<siz[y]) swap(x,y);
ans=max(ans,query(rt[y],rt[x],0)+height[id[i]]);
siz[x]+=siz[y]; fa[y]=x;
merge(rt[y],rt[x]);
}
printf("%d\n",ans);
return 0;
}