Hossam has an unweighted tree GG with letters in vertices.
Hossam defines s(v,u)s(v,u) as a string that is obtained by writing down all the letters on the unique simple path from the vertex vv to the vertex uu in the tree GG.
A string aa is a subsequence of a string ss if aa can be obtained from ss by deletion of several (possibly, zero) letters. For example, "dores", "cf", and "for" are subsequences of "codeforces", while "decor" and "fork" are not.
A palindrome is a string that reads the same from left to right and from right to left. For example, "abacaba" is a palindrome, but "abac" is not.
Hossam defines a sub-palindrome of a string ss as a subsequence of ss, that is a palindrome. For example, "k", "abba" and "abhba" are sub-palindromes of the string "abhbka", but "abka" and "cat" are not.
Hossam defines a maximal sub-palindrome of a string ss as a sub-palindrome of ss, which has the maximal length among all sub-palindromes of ss. For example, "abhbka" has only one maximal sub-palindrome — "abhba". But it may also be that the string has several maximum sub-palindromes: the string "abcd" has 44 maximum sub-palindromes.
Help Hossam find the length of the longest maximal sub-palindrome among all s(v,u)s(v,u) in the tree GG.
Note that the sub-palindrome is a subsequence, not a substring.
Input
The first line contains one integer tt (1≤t≤2001≤t≤200) — the number of test cases.
The first line of each test case has one integer number nn (1≤n≤2⋅1031≤n≤2⋅103) — the number of vertices in the graph.
The second line contains a string ss of length nn, the ii-th symbol of which denotes the letter on the vertex ii. It is guaranteed that all characters in this string are lowercase English letters.
The next n−1n−1 lines describe the edges of the tree. Each edge is given by two integers vv and uu (1≤v,u≤n1≤v,u≤n, v≠uv≠u). These two numbers mean that there is an edge (v,u)(v,u) in the tree. It is guaranteed that the given edges form a tree.
It is guaranteed that sum of all nn doesn't exceed 2⋅1032⋅103.
Output
For each test case output one integer — the length of the longest maximal sub-palindrome among all s(v,u)s(v,u).
Example
input
2
5
abaca
1 2
1 3
3 4
4 5
9
caabadedb
1 2
2 3
2 4
1 5
5 6
5 7
5 8
8 9
output
3
5
题意: 有n个字符构成一棵树,求树上的最长回文子序列。
分析: 先考虑怎么求字符串的最长回文子序列,其实就是一个区间dp,dp[i][j] = d[i+1][j-1]+(s[i]==s[j])*2,dp[i][j] = max(max(dp[i+1][j], dp[i][j-1]), dp[i][j]),这样就更新完了。现在把这个状态转移拿到树上,设dp[i][j]表示从第i个字符到第j个字符路径上的最长回文子序列,若lca(i, j)既不是i也不是j,这时候比较简单,和线性下的一样,dp[i][j] = d[fa[i]][fa[j]]+(s[i]==s[j])*2,dp[i][j] = max(max(dp[fa[i]][j], dp[i][fa[j]]), dp[i][j]),若lca等于i或者j中的某一个,那么需要先倍增找到lca下的那个点,然后更新过程就和刚才一样了。最后注意dp状态要用记忆化搜索的方式更新,直接遍历dp数组更新不具有后效性。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
using namespace std;
char s[2005];
vector<int> to[2005];
int dp[2005][2005], fa[2005][25], dep[2005];
void dfs(int now, int pre)
{
dep[now] = dep[pre]+1;
fa[now][0] = pre;
for(int i = 1; i <= 20; i++)//超出范围的祖先都是0号结点
fa[now][i] = fa[fa[now][i-1]][i-1];
for(int i = 0; i < to[now].size(); i++)
if(to[now][i] != pre)
dfs(to[now][i], now);
}
int lca(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
//先把x提升到和y同样的深度
for(int i = 20; i >= 0; i--)
if(dep[fa[x][i]] >= dep[y])
x = fa[x][i];
//如果此时x和y已经是lca,接下来无法到达lca下一层,若不特判一定出错
if(x == y) return x;
//然后x和y同时提升到lca的下一层
for(int i = 20; i >= 0; i--)
if(fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
int ddfs(int x, int y){//记忆化搜索更新状态
if(y < x) swap(x, y);
if(dp[x][y]) return dp[x][y];
int ans = 0;
int LCA = lca(x, y);
if(LCA != x && LCA != y){
ans = ddfs(fa[x][0], fa[y][0])+(s[x]==s[y])*2;
ans = max(max(ddfs(fa[x][0],y), ddfs(x, fa[y][0])), ans);
}
else{
if(LCA == x){
//找到LCA下面一层的点
int now = y;
for(int i = 20; i >= 0; i--)
if(dep[fa[now][i]] > dep[LCA])
now = fa[now][i];
ans = ddfs(fa[y][0], now)+(s[x]==s[y])*2;
ans = max(max(ddfs(fa[y][0], x), ddfs(y, now)), ans);
}
else{
swap(x, y);
//找到LCA下面一层的点
int now = y;
for(int i = 20; i >= 0; i--)
if(dep[fa[now][i]] > dep[LCA])
now = fa[now][i];
ans = ddfs(fa[y][0], now)+(s[x]==s[y])*2;
ans = max(max(ddfs(fa[y][0], x), ddfs(y, now)), ans);
swap(x, y);
}
}
return dp[x][y] = ans;
}
signed main(){
int T;
cin >> T;
while(T--){
int n;
scanf("%d%s", &n, s+1);
for(int i = 1; i <= n; i++)
to[i].clear();
for(int i = 1; i < n; i++){
int u, v;
scanf("%d%d", &u, &v);
to[u].push_back(v);
to[v].push_back(u);
}
for(int i = 0; i <= n; i++)
for(int j = 0; j <= n; j++)
if(i == j) dp[i][j] = 1;
else dp[i][j] = 0;
dfs(1, 0);
for(int i = 1; i <= n; i++){
int x = i, y = fa[x][0];
if(x > y) swap(x, y);
dp[x][y] = (s[x]==s[y])?2:1;
}
int ans = 1;
for(int i = 1; i <= n; i++)
for(int j = i+1; j <= n; j++){
dp[i][j] = ddfs(i, j);
ans = max(dp[i][j], ans);
}
// for(int i = 1; i <= n; i++, putchar('\n'))
// for(int j = 1; j <= n; j++)
// cout << dp[i][j] << " ";
printf("%d\n", ans);
}
return 0;
}