1. 程式人生 > 實用技巧 >Luogu P4683【IOI2008】Type Printer 印表機|trie

Luogu P4683【IOI2008】Type Printer 印表機|trie

【IOI2008】Type Printer 印表機|trie

連結

題意:一個印表機,支援向其加減一個字母和列印的操作。給出\(N\)個單詞,求列印這些單詞的最小運算元。注意,列印結束後允許印表機內有字母,且列印次序任意。

\(1\le N \le 25000\),單詞均小寫,最大長度20

分析:由於列印操作是無法省去的,運算元最小就要在加減字母下功夫。顯然若兩單詞有相同字首,則可以通過共用字首的方式解決。這樣可減少一次加減共兩個操作。

同時,由於最後一個單詞列印後可不刪除,因此最後一個單詞要儘量長

根據相同字首這一條件,我們聯想到結構與此一致的trie,故使用Trie維護單詞。

可以證明,本題答案就是trie上節點個數*2(加,減各一次)+單詞數-最長單詞長度。

那麼接下來就是列印方案了,這裡採用將最後列印的單詞的節點最後遍歷,其他的先遍歷(顯然不影響答案),在遍歷到單詞的結束標記時列印的方式解決。

上程式碼

#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學習筆記