UPC--6360: 词韵【字典树树上DP】

6360: 词韵

时间限制: 2 Sec  内存限制: 128 MB
提交: 157  解决: 25
[提交] [状态] [讨论版] [命题人:admin]

题目描述

Adrian 很喜欢诗歌中的韵。他认为,两个单词押韵当且仅当它们的最长公共 后缀的长度至少是其中较长单词的长度减一。也就是说,单词 A 与单词 B 押韵 当且仅当 LCS(A, B) ≥ max(|A|, |B|) – 1。(其中 LCS 是最长公共后缀 longest common suffix 的缩写)
现在,Adrian 得到了 N 个单词。他想从中选出尽可能多的单词,要求它们能 组成一个单词序列,使得单词序列中任何两个相邻单词是押韵的。

输入

第一行是一个整数N。
接下来N行,每行一个由小写英文字母组成的字符串,表示每个单词。所有单词互不相同。

输出

输出一行,为一个整数,表示最长单词序列的长度。

样例输入

5
ask
psk
k
krafna
sk
​

样例输出

4

提示

一种最长单词序列是 ask-psk-sk-k。
30%的测试数据:1 ≤ N ≤ 20,所有单词长度之和不超过 3 000。
100%的测试数据:1 ≤ N ≤ 500 000,所有单词长度之和不超过 3 000 000。

wc,看了几乎一天才稍微有眉目大体写出来漏洞百出,看着大佬的题解一步一步改。

大佬的博客具体讲解在里面

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define IO ios::sync_with_stdio(false),cin.tie(0)
#define FIN freopen("D://code//in.txt", "r", stdin)
#define ppr(i,x,n) for(int i = x;i <= n;i++)
#define rpp(i,n,x) for(int i = n;i >= x;i--)
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;

inline int read() {//读入挂
    int ret = 0, c, f = 1;
    for(c = getchar(); !(isdigit(c) || c == '-'); c = getchar());
    if(c == '-') f = -1, c = getchar();
    for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
    if(f < 0) ret = -ret;
    return ret;
}
int dp[maxn],fdp[maxn];
int head[maxn];
int val[maxn];
int cnt;
struct node
{
	int v;
	int next;
}edge[maxn];
void init()
{
	memset(head,-1,sizeof(head));
	memset(dp,0,sizeof(dp));
	memset(fdp,0,sizeof(fdp));
}
void add(int u,int v)
{
	edge[cnt].v = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
	cnt++;
}
void buildtree(string s)
{
	int u = 0;
	for(int i = 0;i < s.size();i++)
	{
		bool flag = false;
		int ch = s[i] - 'a';
		
		for(int j = head[u];j != -1;j = edge[j].next)
		{
		//	cout<<j<<endl;
			if(edge[j].v == ch)
			{
				flag = true;
				u = j;
				break;
			}
		}
		if(!flag)
		{
			add(u,ch);
			u = cnt-1;
		}
	}
	val[u]++;
}
void dfs(int u)
{
	//cout<<u<<endl;
	int tr[2] = {0};
	int mark = 0;
	for(int i = head[u];i != -1;i = edge[i].next)
	{
		dfs(i);
		int trchild = fdp[i] - val[i];//不包括i本身的最长链
		if(trchild > tr[1])
		{
			tr[0] = tr[1];tr[1] = trchild;
		}
        else if(trchild > tr[0])
			tr[0] = trchild;
		
        mark += val[i];
	}
	mark += val[u];
    if(tr[0] > 0)
		dp[u] = tr[0] + tr[1] + mark; //连接两孩子
    if(val[u])
		fdp[u]=mark+tr[1]; //最长单链
}
int main()
{
	IO;
	init();
	int n;
	cin>>n;
	cnt = 1;
	ppr(i,1,n)
	{
		string s;
		cin >> s;
		reverse(s.begin(),s.end());
		//cout<<s<<endl;
		buildtree(s);
	}
	
	dfs(0);
	int ans = 0;
//	ppr(i,0,cnt-1)
//	cout<<dp[i]<<" "<<fdp[i]<<endl;
	ppr(i,0,cnt-1)
	{
		ans = max(ans,max(dp[i],fdp[i]));
	}
	cout << ans <<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Pandapan1997/article/details/81253074
UPC