唉,又这样贡献了一个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); } }