随笔 - 216,  文章 - 0,  评论 - 17,  阅读 - 25574

题目链接:

https://www.luogu.com.cn/problem/P7537

题意:

记字符串 AB 的最长公共后缀为 LCS(A,B)
LCA(A,B)>=max(|A|,|B|)1 时,认为这两个字符串是押韵的。
给定 n 个字符串(两两不相同),要求从其中组合出一个长度最长的字符串序列,使得序列中相邻的两个字符串押韵。

思路:

容易想到,将字符串翻转一下,后缀就变成了前缀,然后将字符串挂到字典树上。
如果要满足两个字符串的最大公共前缀长度 >= 最长的字符串的长度 - 1,只有两种情况。
设字符串为 S,字符为 C
那么只可能是 S+C1S+C2 或者 SS+C
dp[u] 为以字符 u 为结尾的字符串中,押韵字符串的最大数量。
对于一个字符串,要找最大押韵序列,它可以从自己的子节点转移过来,但是不能所有的节点都转移过来,只能有两个,如下图这种方式。

最中间的字符串找到的最大的押韵序列,就是它子节点中最大的两个孩子的数量之和。
同时,它也可以用剩余孩子,但是不能用它们的孩子。
比如:
a
ab
abc
abcd
ac
acd
acde
ad
ade
以上为翻转了之后的字符串,答案为 8。
所以对于 u,它的最大值就是最大的两个孩子的数量 + 自己 + 剩余有的孩子的数量。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 3e6 + 10;
char s[N];
LL ans;
struct Trie{
	LL trie[N][26], cnt[N], dp[N], idx = 0;
	void insert(char s[]){
		LL u = 0, len = strlen(s);
		for (int i = len - 1; i >= 0; i -- ){
			LL v = s[i] - 'a';
			if (!trie[u][v]) trie[u][v] = ++ idx;
			u = trie[u][v];
		}
		cnt[u] ++ ;
	}
	void dfs(LL u){
		LL mx1 = 0, mx2 = 0, count = 0;
		for (int i = 0; i < 26; i ++ ){
			LL v = trie[u][i];
			if (!v) continue;
			dfs(v);
			count += cnt[v];
			if (dp[v] > mx1){
				mx2 = mx1;
				mx1 = dp[v];
			}
			else if (dp[v] > mx2){
				mx2 = dp[v];
			}
		}
		if (cnt[u])
			dp[u] = mx1 + max(count, 1LL);
		ans = max(ans, mx1 + mx2 + max(0LL, count - 2) + cnt[u]);
	}
}trie;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL n;
	cin >> n;
	for (int i = 0; i < n; i ++ ){
		cin >> s;
		trie.insert(s);
	}
	trie.dfs(0);
	cout << ans << "\n";
	return 0;
}
posted on   Hamine  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示