传送门:点击打开链接
题意:告诉你一棵树,每个节点有一个字母,然后要求分别输出每个节点到根节点这条路径上的不重复的子串个数。
思路:因为是树形结构,我们就没办法用后缀数组搞了,必须要想办法能动态维护就好啦
我们这里用后缀平衡树来做到动态维护SA
首先我们要明确这道题的思路,每增加一个节点,根据后缀数组的知识我们知道,会多增加l-max(H[i],H[i+1])个子串,H表示高度数组,也就是两个相邻的SA的最长前缀
问题就出来了,我们如何来动态维护SA
我们用一个set来搞,set相当于一颗平衡树,里面是按照SA排序好的,现在我们来增加一个节点,我们要在字符串的前面增加这个节点,这样就相当于后缀都不变,我又多增加了一条串,因为现在set里面是有序的,所以我只要能做到比较后缀的字典序大小关系,我就能够快速把它插入到set中去。
比较后缀的字典序大小,我们用经典的二分+hash求lcp的方法来搞定
不得不说字符串hash如果都用unsigned long long来存的话还真方便,减法乘法啥的都不需要取模~
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;
const int MX = 2e4 + 5;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int seed = 131;
typedef unsigned long long ULL;
struct Edge {
int v, nxt;
} E[MX];
int Head[MX], rear;
void edge_init() {
rear = 0;
memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v) {
E[rear].v = v;
E[rear].nxt = Head[u];
Head[u] = rear++;
}
int n;
char S[MX];
ULL Hash[MX], F[MX];
LL ans[MX];
bool check(int u, int v, int m) {
ULL h1 = Hash[u] - Hash[u - m] * F[m];
ULL h2 = Hash[v] - Hash[v - m] * F[m];
return h1 == h2;
}
int lcp(int u, int v) {
int l = 0, r = min(u, v), m;
while(l <= r) {
m = (l + r) >> 1;
if(check(u, v, m)) l = m + 1;
else r = m - 1;
}
return l - 1;
}
struct cmp {
bool operator()(const int &a, const int &b) {
int p = lcp(a, b);
if(a == p && b != p) return true;
if(b == p && a != p) return false;
int u = Hash[a - p] - Hash[a - p - 1] * seed;
int v = Hash[b - p] - Hash[b - p - 1] * seed;
return u < v;
}
};
set<int, cmp>W;
void DFS(int u, int f, int l, LL s, ULL h) {
h = h * seed + S[u]; Hash[l] = h;
set<int, cmp>::iterator e = W.insert(l).first, e1 = e, e2 = e;
int Max = 0;
if(e1 != W.begin()) e1--, Max = max(Max, lcp(l, *e1));
if(++e2 != W.end()) Max = max(Max, lcp(l, *e2));
s += l - Max; ans[u] = s;
for(int i = Head[u]; ~i; i = E[i].nxt) {
int v = E[i].v;
if(v == f) continue;
DFS(v, u, l + 1, s, h);
}
W.erase(l);
}
int main() {
F[0] = 1; //FIN;
for(int i = 1; i < MX; i++) {
F[i] = F[i - 1] * seed;
}
int T; scanf("%d", &T);
while(T--) {
W.clear();
edge_init();
scanf("%d", &n);
for(int i = 1; i <= n - 1; i++) {
int u, v;
scanf("%d%d", &u, &v);
edge_add(u, v);
edge_add(v, u);
}
scanf("%s", S + 1);
DFS(1, -1, 1, 0, 0);
for(int i = 1; i <= n; i++) {
printf("%I64d\n", ans[i]);
}
}
return 0;
}