Luogu P4683【IOI2008】Type Printer 打印机|trie

【IOI2008】Type Printer 打印机|trie

链接

题意:一个打印机,支持向其加减一个字母和打印的操作。给出\(N\)个单词,求打印这些单词的最小操作数。注意,打印结束后允许打印机内有字母,且打印次序任意。

\(1\le 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\n";
	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<<"-\n";
		}
	}
	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学习笔记

posted @ 2020-08-08 20:44  fmj_123  阅读(121)  评论(0编辑  收藏  举报