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