唉,又这样贡献了一个WA,忘了把自己的用来测试的输出删了……
一开始还真被这题目吓住了,不过花点心思想想,还是可以做出来的
题意要求的是打印单词的最少操作数
只有三个操作:
输入,删除,还有打印,前缀就不需要重复输入,想到了字典树,这道题目也就好做了
要 求最少的操作数,所以首先要归类,所有拥有相同前缀的单词,放在一块输入,题目还说,最后一次打印不需要删除
理论上是像上面说的,但实际我们在计算最少操作数时,可以这样算,首先假设所有的单词都需要重新打印还有删除,所以总操作数是单词总长度*2
接下来,计算出前缀,可以这样想,只需计算出每一个节点被哪些单词共有,如题目中的样例,“freeradiant”,“freeopen”,f 被俩个节点共有,所以省了一步删除还有一步输入,所以就是(2-1)*2;
最后再找 出最长的一个单词的长度max,很容易理解,最后一行不用删除,那最后一行肯定是越长越好,
根据这三个数值,可以发现,最少的操作=总操作数-总共可以省掉的操作数-多删除掉的最后一行的长度(max)
在代码实现上,额,125ms,不是很快,还是用到了栈还有字典树
更具体的解释,看代码吧
#include<iostream>
#include<stack>
#include<string>
using namespace std;
struct node
{
node *next[26];
int v;//记录有多少个单词经过该节点,或者说,有多少单词拥有到该节点为止的前缀
int lev;//层数,好像用处不是很大
};
node *root;
void insert(char *s)//插入操作
{
node *p=root;
for(;*s!='\0';s++)
{
int d=*s-'a';
if(p->next[d]!=NULL)
{
p=p->next[d];
p->v++;
}
else
{
p->next[d]=new node();
p=p->next[d];
p->v++;
}
}
}
int find()//遍历整棵树
{
node *p=root,*cur=root,*nex;
int sum=0;
stack<node*> nd;
cur->lev=0;
nd.push(cur);
while(!nd.empty())
{
cur=nd.top();
nd.pop();
if(cur->lev>0)//根节点不计算
{
sum+=(cur->v-1)*2;
}
for(int i=0;i<26;i++)
{
nex=cur->next[i];
if(nex!=NULL&&nex->v>1)//单词数大于一,表示有可以省掉的操作,才有必要进行计算,
{
nex->lev=cur->lev+1;
nd.push(nex);//压入栈
}
}
}
return sum;
}
void del(node *p)//释放空间
{
for(int i=0;i<26;i++)
{
if(p->next[i]!=NULL)
del(p->next[i]);
}
delete(p);
}
int main()
{
int sum,i,j,n,max,len;
char str[55];
while(scanf("%d",&n)!=EOF)
{
sum=max=0;
root=new node();
for(i=0;i<n;i++)
{
scanf("%s",str);
insert(str);
len=strlen(str);
if(len>max)//找出最长的单词
max=len;
sum+=len;//计算单词总长度
}
j=find();
sum=sum*2-j-max+n;
cout<<sum<<endl;
del(root);
}
}