问题描述:
字母(Trie)树是一个表示一个字符串集合中所有字符串的前缀的数据结构,其有如下特征:
1.树的每一条边表示字母表中的一个字母
2.树根表示一个空的前缀
3.树上所有其他的节点都表示一个非空前缀,每一个节点表示的前缀为树根到该节点的路径上所有字母依次连接而成的字符串。
4.一个节点的所有出边(节点到儿子节点的边)中不存在重复的字母。
现在Matej手上有N个英文小写字母组成的单词,他想知道,如果将这N个单词中的字母分别进行重新排列,形成的字母树的节点数最少是多少。
输入格式:
第一行包含一个正整数N(1<=N<=16)
接下来N行每行一个单词,每个单词都由小写字母组成。
单词的总长度不超过1,000,000。
输出格式:
输出仅一个正整数表示N个单词经过重新排列后,字母树的最少节点数。
样例输入输出:
10
jgda
dbfdjj
hehegdfh
faeejic
acagdgfcjc
jifiigdbif
fdbdii
ch
c
adccdd
42
问题描述专门大肆宣传了一番trie还是naive了,题目后面加了一个trie也是too young。 这明显就是个状压DP,n<=16如此醒目吼吗。贴一波代码。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int sums=(1<<17),inf=1e9,maxn=1000050;
int n,len[30],i,j,k,b[30][30],f[sums];
char a[maxn];
int main()
{
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%s",a+1);
len[i]=strlen(a+1);
//cout<<len[i]<<endl;
for(j=1;j<=len[i];j++)
b[i][a[j]-'a']++;//cout<<a[j]-'a'<<endl;
}
f[0]=inf;
for(i=0;i<(1<<n);i++)//枚举所有状态
{
int sum=0;
for(j=0;j<n;j++)
{
if((1<<j)&i)f[i]+=len[j];//记录至多需要的节点数
}
for(k=0;k<=25;k++)
{
int minn=inf;
for(j=0;j<n;j++)
if((1<<j)&i)
minn=min(minn,b[j][k]);
sum+=minn;//记录当前集合中每种字母在单一串中出现的最小值之和
}
for(j=i;j;j=(j-1)&i)
{
f[i]=min(f[i],f[j]+f[i^j]);
}
if(f[i]>sum)f[i]-=sum;//有了前面求的sum,就可以直接更新f[i]
}
printf("%d",f[(1<<n)-1]+1);//记得要+1,字典树。。。。。
}