[记忆化搜索]Hossam and (sub-)palindromic tree Codeforces1771D

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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值