P4683 [IOI 2008] Type Printer

题意

\(n\) 个长度不超过 \(20\) 的字符串,有一个奇妙的打印机,三种操作:在打印机当前词的末端(尾部)添加一个字母;在打印机当前词的尾部删去一个字母(将打印机当前词的最后一个字母删去)。仅当打印机当前至少有一个字母时才允许进行该操作;将打印机上的当前词打印出来。求按照任意顺序把 \(n\) 个字符串打印出来的最小操作次数,并给出一种方案。
序列的描述方法如下:

  • 添加一个字母,用这个小写字母的自身来表示。
  • 删去一个字母,用 - 表示。
  • 打印单词,用 P 表示。

\(n\le2.5*10^4\)

思路

把字典树建出来,在上面 \(DFS\)。设当前在点 \(u\)。如果 \(u\)\(g\) 个字符串的结尾,就输出 \(g\)P。按照子树内最大深度从小到大遍历 \(u\) 的所有儿子,如果存在,就把该儿子对应的字符添加在末尾,向下递归,结束后把该字符删除。
现在得到了一个操作序列,但祂不是最优的,因为最后有很多删除,使得祂回到了原点。那么把序列末尾的 - 全部删除,直到出现第一个 P。由于在结束节点是先输出 P 再向下递归,并且遍历儿子时按照子树内最大深度从小到大遍历,最后输出的那个字符串一定是最长的一个字符串之一,使得节省的步数最大化,所以是最优的。

代码

// Problem: P4683 [IOI 2008] Type Printer
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4683
// Memory Limit: 62 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=25010;
int n;
vector<char>ans;
class Trie{
private:
    struct node{
        int ch[26];
        char val,deep;
        short end;
    }t[maxn*20];
    int cnt=1;
    void dfs(int u){
        if(u==0) return ;
        t[u].deep=1;
        for(int i=0;i<26;i++){
            dfs(t[u].ch[i]);
            t[u].deep=max<char>(t[u].deep,t[t[u].ch[i]].deep+1);
        }
        sort(t[u].ch,t[u].ch+26,[&](int a,int b){return t[a].deep<t[b].deep;});
    }
    void dfs2(int u){
        if(u==0) return;
        for(int i=1;i<=t[u].end;i++) ans.push_back('P');
        for(int i=0;i<26;i++){
            if(!t[u].ch[i]) continue;
            ans.push_back(t[t[u].ch[i]].val);
            dfs2(t[u].ch[i]);
            ans.push_back('-');
        }
    }
public:
    void insert(string s){
        int u=1;
        for(int i:s){
            if(!t[u].ch[i-'a']) t[u].ch[i-'a']=++cnt,t[cnt].val=i;
            u=t[u].ch[i-'a'];
        }
        t[u].end++;
    }
    void calc(){
        dfs(1);
        dfs2(1);
        while(ans.back()=='-') ans.pop_back();
    }
}tr;
signed main(){
    read(n);
    for(int i=1;i<=n;i++){
        string s;read(s);
        tr.insert(s);
    }
    tr.calc();
    write(ans.size()),write("\n");
    for(char i:ans) write(i),write("\n");
    return 0;
}
posted @ 2026-05-19 20:54  Link-Cut_Trees  阅读(17)  评论(0)    收藏  举报