题目链接:

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

题意:

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

思路:

容易想到,将字符串翻转一下,后缀就变成了前缀,然后将字符串挂到字典树上。
如果要满足两个字符串的最大公共前缀长度 >= 最长的字符串的长度 - 1,只有两种情况。
设字符串为 \(S\),字符为 \(C\)
那么只可能是 \(S + C_1\)\(S + C_2\) 或者 \(S\)\(S + 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 2022-07-07 21:02  Hamine  阅读(46)  评论(0编辑  收藏  举报