问题描述
魔族现在使用一种新型的密码系统。每一个密码都是一个给定的仅包含小写字母的英文单词表,每个单词至少包含1个字母,至多75个字母。如果在一个由一个词或多个词组成的表中,除了最后一个以外,每个单词都被其后的一个单词所包含,即前一个单词是后一个单词的前缀,则称词表为一个词链。例如下面单词组成了一个词链:
i
int
integer
但下面的单词不组成词链:
integer
intern
现在你要做的就是在一个给定的单词表中取出一些词,组成最长的词链,就是包含单词数最多的词链。将它的单词数统计出来,就得到密码了。
也就是说密码就是最长词链所包括的单词数阿……
输入格式
第一行为单词表中的单词数N(1<=N<=2000),下面每一行有一个单词,按字典顺序排列,中间也没有重复的单词。
输出格式
只有一行,一个整数,表示输出的密码
样例输入
5
i
int
integer
intern
internet
样例输出
4
1.trie
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
using namespace std;
int N;
struct note
{
int len;
int son[26];
}tree[1000000];
int ans=-999999;
char S[5000];
int tot=1;
int a;
void insert(char O[])
{
int i,t,p=1;
int cnt=0;
int len=strlen(O);
for(i=0;i<len;i++)
{
t=O[i]-'a';
if(tree[p].son[t]==0)
{
tot++;
tree[p].son[t]=tot;
p=tot;
}
else {p=tree[p].son[t];cnt=max(tree[p].len,cnt);}
}
tree[p].len=cnt+1;
//cout<<tree[p].len<<endl;
ans=max(ans,cnt+1);
}
int main()
{
int b,c,d,e;
scanf("%d",&N);
for(a=1;a<=N;a++)
{
scanf("%s",&S);
insert(S);
}
cout<<ans;
return 0;
}
2.dp
那么我们的dp怎么写呢?作为一只初学dp的蒟蒻,老师告诉我需要考虑四点问题:
-
状态:f_ifi 表示以第ii号位置结尾最多可以取的单词数
-
答案:f_nfn
-
转移:f_i = max(f_j+1,f_i)fi=max(fj+1,fi)
-
初值:f_i = 1fi=1
那接下来的事情就很简单了,按照上述思路写代码就好啦,AC代码奉上:
#include<bits/stdc++.h>
using namespace std;
int n, f[2010], ans;
string s[2010];
int main()
{
scanf ("%d", &n);
for (int i=1; i<=n; i++)
cin >>s[i];
for (int i=1; i<=n; i++)
{
f[i] = 1;
for (int j=1; j<i; j++)
if (s[j]==s[i].substr(0,s[j].size())) //从s[i]的0号位置截取s[j]长度的子串,判断是否与s[j]相等。
f[i] = max(f[j]+1, f[i]);
ans = max(f[i], ans);
}
printf ("%d", ans);
return 0;
}
3.STL
map<>
#include<iostream>
#include<map>
using namespace std;
map<string,int> f;
int n,ans;
string s[2005];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>s[i];
f[s[i]]++;
}
for(int i=1;i<=n;i++){
int sum=0;
string a;
int d=s[i].size();
for(int j=0;j<d;j++){
a+=s[i][j],sum+=f[a];
}
ans=max(ans,sum);
}
cout<<ans;
}