【IOI2008】Type Printer 打印机|trie
题意:一个打印机,支持向其加减一个字母和打印的操作。给出(N)个单词,求打印这些单词的最小操作数。注意,打印结束后允许打印机内有字母,且打印次序任意。
(1le N le 25000),单词均小写,最大长度20
分析:由于打印操作是无法省去的,操作数最小就要在加减字母下功夫。显然若两单词有相同前缀,则可以通过共用前缀的方式解决。这样可减少一次加减共两个操作。
同时,由于最后一个单词打印后可不删除,因此最后一个单词要尽量长。
根据相同前缀这一条件,我们联想到结构与此一致的trie,故使用Trie维护单词。
可以证明,本题答案就是trie上节点个数*2(加,减各一次)+单词数-最长单词长度(省略‘-’)。
那么接下来就是打印方案了,这里采用将最后打印的单词的节点最后遍历,其他的先遍历(显然不影响答案)的方法,在进入节点时输出字母,退出时输出-
,同时若该节点有单词的结束标记时打印P
即可解决。
上代码
#include<bits/stdc++.h>
using namespace std;
int n,len,num,maxl,maxn,g,ans;string st[30000];
struct trie
{
int L[26];int e;
}tree[901000];
void addtrie(int x,int y)
{
if (y==len)
{
tree[x].e=true;
return ;
}
int ne=st[num][y]-'a';
if (!tree[x].L[ne])
{
g++;
tree[x].L[ne]=g;
}
addtrie(tree[x].L[ne],y+1);
}
void printrie(int x,int y,bool o)
{
int ne=0;
if (!o) ne=26; else ne=st[maxn][y]-'a';
if (tree[x].e) cout<<"P
";
for (int i=0;i<26;i++)
{
if (i==ne) continue;
if (tree[x].L[i])//有就遍历
{
cout<<char(i+'a')<<endl;
printrie(tree[x].L[i],y+1,0);
cout<<"-
";
}
}
if (o&&y!=maxl)//最后遍历最长单词
{
cout<<char(ne+'a')<<endl;
printrie(tree[x].L[ne],y+1,1);
}
}
int main()
{
cin>>n;
for (int i=1;i<=n;i++)
{
cin>>st[i];
num=i;len=st[i].size();
if (maxl<len)
{
maxl=max(maxl,len);
maxn=i;
}
addtrie(0,0);//建trie
}
ans=g*2+n-maxl;
cout<<ans<<endl;
printrie(0,0,1);
return 0;
}
广告Trie学习笔记