Trie树

问题 H: Trie树

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

题目描述

字母(Trie)树是一个表示一个字符串集合中所有字符串的前缀的数据结构,其有如下特征:
1.树的每一条边表示字母表中的一个字母
2.树根表示一个空的前缀
3.树上所有其他的节点都表示一个非空前缀,每一个节点表示的前缀为树
根到该节点的路径上所有字母依次连接而成的字符串。
4.一个节点的所有出边(节点到儿子节点的边)中不存在重复的字母。
 
单词“A”“to”“tea”“ted”“ten”“i”“in”“inn”对应的Trie树
现在Matej手上有N个英文小写字母组成的单词,他想知道,如果将这N个单词中的字母分别进行重新排列,形成的字母树的节点数最少是多少。

 

输入

第一行包含一个正整数N(1<=N<=16)
接下来N行每行一个单词,每个单词都由小写字母组成。
单词的总长度不超过1,000,000。
 

 

输出

输出仅一个正整数表示N个单词经过重新排列后,字母树的最少节点数。

 

样例输入

3
a
ab
abc

 

样例输出

4

 思路:状态压缩DP,利用二进制位模拟字符串的集合。小于2^n的每一个数都可以表示一个集合,因为这个数二进制位上的1代表对应的字符串被选择,0代表未被选择。就拿样例来说,(2^3-1:1 2 3 4 5 6 7)其中3的二进制位为11,表示a,ab组成的集合;7的二进制位为111,表示a,ab,abc组成的集合!

对于解法,咱们可以两两结合后再与其他的结合!还要注意这句高亮的代码:

for(int j=i&(i-1);j;j=(j-1)&i)
{
f[i]=min(f[i],f[j]+f[i^j]);
}

遍历子集,对于两个子集(f[j],f[i^j])求和取最小值,好好理解下代码吧!

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int sums=(1<<17),inf=1e9+7,maxn=1000030;
int n,b[20][30],f[sums],len[20],sum,minn;
char a[maxn];
int main()
{
scanf("%d",&n);
for(int i=0;i<=n-1;i++)
{
scanf("%s",a);
len[i]=strlen(a);
for(int j=0;j<len[i];j++)
{
b[i][a[j]-'a']++;
}
}
for(int i=1;i<(1<<n);i++)
{
sum=0;
for(int j=0;j<=n-1;j++)
{
if((1<<j)&i)
{
f[i]+=len[j];
}
}
for(int k=0;k<=25;k++)
{
minn=inf;
for(int j=0;j<=n-1;j++)
{
if((1<<j)&i)
{
minn=min(minn,b[j][k]);
}
}
sum+=minn;
}
for(int j=i&(i-1);j;j=(j-1)&i)
{
f[i]=min(f[i],f[j]+f[i^j]);
}
if(f[i]>sum)
{
f[i]-=sum;
}
}
printf("%d\n",f[(1<<n)-1]+1);
}

 

posted @ 2018-07-31 10:04  lglh  阅读(100)  评论(0编辑  收藏  举报