Description
定义这个字符串以第 i i 个字符开头的后缀为后缀(编号从 1 11 开始),每个后缀 i i 都有一个权值,同时定义两个后缀 i,j i , j (i≠j) ( i ≠ j ) 的贡献为它们的最长公共前缀长度加上它们权值的异或和,也就是 LCP(i,j)+(wixorwj) L C P ( i , j ) + ( w i x o r w j ) 。而你的任务就是,求出这个字符串的所有后缀两两之间贡献的最大值。
Solution
首先进行后缀排序,
考虑分治,对于区间 [L,R] [ L , R ] ,找到其 height h e i g h t 最小值的位置 minpos m i n p o s ,那么对于横跨 minpos m i n p o s 的位置,他们的 LCP L C P 已经确定了。
所以可以枚举 [L,minpos] [ L , m i n p o s ] 与 (minpos,R] ( m i n p o s , R ] 中较小的一段中的数,然后在可持久化Trie中查询异或最大值。这样处理的复杂度为 O(minsizelog2maxsize) O ( m i n s i z e l o g 2 m a x s i z e )
总时间复杂度相当于逆向的启发式合并,为 O(nlog2n) O ( n l o g 2 n )
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
int n, ans, w[maxn];
char s[maxn];
inline int gi()
{
char c = getchar();
while (c < '0' || c > '9') c = getchar();
int sum = 0;
while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
namespace sa
{
#define cmp(a, b) (y[a] == y[b] && y[a + j] == y[b + j])
int m = 26, p, x[maxn], y[maxn], c[maxn], sa[maxn], rank[maxn];
void build_sa()
{
for (int i = 1; i <= n; ++i) ++c[x[i] = s[i] - 'a' + 1];
for (int i = 2; i <= m; ++i) c[i] += c[i - 1];
for (int i = 1; i <= n; ++i) sa[c[x[i]]--] = i;
for (int j = 1; j <= n; j <<= 1) {
p = 0;
for (int i = n; i >= n - j + 1; --i) y[++p] = i;
for (int i = 1; i <= n; ++i)
if (sa[i] > j) y[++p] = sa[i] - j;
for (int i = 1; i <= m; ++i) c[i] = 0;
for (int i = 1; i <= n; ++i) ++c[x[y[i]]];
for (int i = 2; i <= m; ++i) c[i] += c[i - 1];
for (int i = n; i >= 1; --i) sa[c[x[y[i]]]--] = y[i];
swap(x, y);
x[sa[1]] = p = 1;
for (int i = 2; i <= n; ++i)
x[sa[i]] = cmp(sa[i - 1], sa[i]) ? x[sa[i - 1]] : ++p;
if (p >= n) break;
m = p;
}
for (int i = 1; i <= n; ++i) rank[sa[i]] = i;
}
int ht[maxn];
void build_ht()
{
for (int i = 1, k = 0; i <= n; ++i) {
if (k) --k;
if (rank[i] == n) continue;
while (s[i + k] == s[sa[rank[i] + 1] + k]) ++k;
ht[i] = k;
}
}
int Log[maxn], Min[20][maxn], pos[20][maxn];
int getmin(int l, int r)
{
--r;
int k = Log[r - l + 1];
if (Min[k][l] < Min[k][r - (1 << k) + 1]) return pos[k][l];
else return pos[k][r - (1 << k) + 1];
}
void build_st()
{
for (int i = 2; i < n; ++i) Log[i] = Log[i >> 1] + 1;
for (int i = 1; i < n; ++i) Min[0][i] = ht[sa[i]], pos[0][i] = i;
for (int j = 0; (1 << j) < (n - 1); ++j)
for (int i = 1; i + (1 << (j + 1)) - 1 < n; ++i) {
if (Min[j][i] < Min[j][i + (1 << j)]) Min[j + 1][i] = Min[j][i], pos[j + 1][i] = pos[j][i];
else Min[j + 1][i] = Min[j][i + (1 << j)], pos[j + 1][i] = pos[j][i + (1 << j)];
}
}
}
namespace trie
{
int root[maxn], ch[maxn * 20][2], sum[maxn * 20], tot;
int insert(int s1, int v)
{
int s2 = ++tot, tmp = s2;
for (int i = 16, t; i >= 0; --i) {
ch[s2][0] = ch[s1][0]; ch[s2][1] = ch[s1][1]; sum[s2] = sum[s1] + 1;
t = (v >> i) & 1;
s1 = ch[s1][t];
s2 = ch[s2][t] = ++tot;
}
sum[s2] = sum[s1] + 1; return tmp;
}
int query(int s1, int s2, int v)
{
int ans = 0;
for (int i = 16, t; i >= 0; --i) {
t = ((v >> i) & 1) ^ 1;
if (sum[ch[s2][t]] - sum[ch[s1][t]]) ans += 1 << i;
else t ^= 1;
s1 = ch[s1][t]; s2 = ch[s2][t];
}
return ans;
}
}
void solve(int l, int r)
{
if (l >= r) return ;
int minpos = sa::getmin(l, r), x, y, L, R;
if (minpos - l + 1 < r - minpos) x = l, y = minpos, L = minpos + 1, R = r;
else x = minpos + 1, y = r, L = l, R = minpos;
int Max = 0;
for (int i = x; i <= y; ++i) Max = max(Max, trie::query(trie::root[L - 1], trie::root[R], w[sa::sa[i]]));
ans = max(ans, Max + sa::ht[sa::sa[minpos]]);
solve(l, minpos);
solve(minpos + 1, r);
}
int main()
{
n = gi();
scanf("%s", s + 1);
sa::build_sa();
sa::build_ht();
sa::build_st();
for (int i = 1; i <= n; ++i) w[i] = gi();
for (int i = 1; i <= n; ++i)
trie::root[i] = trie::insert(trie::root[i - 1], w[sa::sa[i]]);
solve(1, n);
printf("%d\n", ans);
return 0;
}