Codeforces 577E Ann and Half-Palindrome 字典树

题目链接

题意:

若一个字符串是半回文串,则满足第一位和最后一位相等, 第三位和倒数第三位相等,如此类推。

给定一个字符串s,输出s的所有子串中的半回文串字典序第k大的 字符串。


good[i][j] 表示 s(i,j) 是半回文串。

把这些回文串插到字典树里 在字典树上找第k个叶子节点。

插入时:插入以i点开头的所有半回文串。


#include <iostream>
#include <string>
#include <vector>
#include <cstring>
#include <cstdio>
#include <map>
#include <queue>
#include <algorithm>
#include <stack>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
using namespace std;
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x < 0) {
		putchar('-');
		x = -x;
	}
	if (x > 9) pt(x / 10);
	putchar(x % 10 + '0');
}
typedef int ll;
typedef pair<int, int> pii;
const int inf = 1e9;
const int N = 5005;
bool good[N][N];
#define Word_Len 5050000  
#define Sigma_size 2  

struct <span class="KSFIND_CLASS_SELECT" id="0KSFindDIV">Trie</span> {
	ll ch[Word_Len][Sigma_size], sz;     //Word_Len是字典树的节点数 若都是小写字母Sigma_size=26 sz为当前节点数  
	ll Have_word[Word_Len];      //这个节点下有几个单词  
	ll val[Word_Len];                // 这个节点附带的信息,初始化为0表示这个节点不存在单词,所以节点带的信息必须!=0  
	ll pre[Word_Len];
	char he[Word_Len];
	ll Newnode() { memset(ch[sz], 0, sizeof(ch[sz])); val[sz] = Have_word[sz] = 0; return sz++; }
	void init()                          //初始化字典树  
	{
		sz = 0; Newnode();
	}//初始化  
	ll idx(char c) { return c - 'a'; }     //字符串编号  

	int insert(char *s, int start) {     //把v数字加给 s单词最后一个字母  
		ll u = 0;
		for (ll i = 0; s[i]; i++) {
			ll c = idx(s[i]);
			if (!ch[u][c])            //节点不存在就新建后附加  
			{
				ch[u][c] = Newnode();
				he[sz - 1] = s[i];
				pre[sz - 1] = u;
			}
			u = ch[u][c];
			if (good[start][start + i]) Have_word[u]++;
		}
		return u;
	}
	void dfs(int u) {
		val[u] += Have_word[u];
		for (int i = 0; i < Sigma_size; i++)
		{
			int v = ch[u][i];	if (!v)continue;
			dfs(v);
			val[u] += val[v];
		}
	}
	int find_kth(int u, int k) {
		if (u)putchar(he[u]);
		if (k <= Have_word[u])return u;
		k -= Have_word[u];
		for (int i = 0; i < Sigma_size; i++)
		{
			int v = ch[u][i];	if (!v)continue;
			if (k <= val[v])
			{
				return find_kth(v, k);
			}
			else k -= val[v];
		}
	}
} ac;
int n, k;
char s[N];
int main() {
	scanf("%s", s);
	rd(k);
	n = strlen(s);
	for (int i = 0; i < n; i++)
	{
		for (int l = i, r = i; l >= 0 && r < n; l --, r ++) 
		{
			if (s[l] == s[r])
				if (l + 2 >= r - 2 || r - 2 < 0 || l + 2 >= n || good[l + 2][r - 2])
					good[l][r] = true;			
		}
		for (int l = i, r = i + 1; l >= 0 && r < n; l --, r ++)
		{
			if (s[l] == s[r])
				if (l + 2 >= r - 2 || r - 2 < 0 || l + 2 >= n || good[l + 2][r - 2])
				good[l][r] = true;
		}
	}

	ac.init();
	for (int i = 0; i < n; i++)
	{
		int j = n - 1;
		while (good[i][j] == false)j--;
		char c = s[j + 1]; s[j + 1] = 0;
		ac.insert(s + i, i);
		s[j + 1] = c;
	}

	ac.dfs(0);
	ac.val[0] = 0;
	ac.find_kth(0, k);
	return 0;
}


ti
tri
Trie
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值