Ancient Printer
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)
Problem Description
The contest is beginning! While preparing the contest, iSea wanted to print the teams' names separately on a single paper.
Unfortunately, what iSea could find was only an ancient printer: so ancient that you can't believe it, it only had three kinds of operations:
● 'a'-'z': twenty-six letters you can type
● 'Del': delete the last letter if it exists
● 'Print': print the word you have typed in the printer
The printer was empty in the beginning, iSea must use the three operations to print all the teams' name, not necessarily in the order in the input. Each time, he can type letters at the end of printer, or delete the last letter, or print the current word. After printing, the letters are stilling in the printer, you may delete some letters to print the next one, but you needn't delete the last word's letters.
iSea wanted to minimize the total number of operations, help him, please.
Unfortunately, what iSea could find was only an ancient printer: so ancient that you can't believe it, it only had three kinds of operations:
● 'a'-'z': twenty-six letters you can type
● 'Del': delete the last letter if it exists
● 'Print': print the word you have typed in the printer
The printer was empty in the beginning, iSea must use the three operations to print all the teams' name, not necessarily in the order in the input. Each time, he can type letters at the end of printer, or delete the last letter, or print the current word. After printing, the letters are stilling in the printer, you may delete some letters to print the next one, but you needn't delete the last word's letters.
iSea wanted to minimize the total number of operations, help him, please.
Input
There are several test cases in the input.
Each test case begin with one integer N (1 ≤ N ≤ 10000), indicating the number of team names.
Then N strings follow, each string only contains lowercases, not empty, and its length is no more than 50.
The input terminates by end of file marker.
Each test case begin with one integer N (1 ≤ N ≤ 10000), indicating the number of team names.
Then N strings follow, each string only contains lowercases, not empty, and its length is no more than 50.
The input terminates by end of file marker.
Output
For each test case, output one integer, indicating minimum number of operations.
Sample Input
2 freeradiant freeopen
Sample Output
21HintThe sample's operation is: f-r-e-e-o-p-e-n-Print-Del-Del-Del-Del-r-a-d-i-a-n-t-Print
Author
iSea @ WHU
Source
题意:有三种操作,为删除一个字符,输出一个字符串,输入一个字符
给你n个字符串,求最少的操作能够输出所有的单词
方法一:
思路:可以发现这是一个字典树的题,
要形成一个新的单词,一定是现在的单词删除字符到和新的单词具有相同的根的地方,
所以长度最大的单词一定是最后进行操作的。
同时,我们经过观察可以发现,除了最后的一个单词在字典树形成的路径之外,
其他的单词一定是先形成它,然后再删除,它在字典树上的路径都会经过两次(除了和最长单词的重合部分之外)
所以答案便很容易找到(sz-1)*2-maxv(最长单词长度),sz-1是因为构造字典树初始化时sz为1
#include <cstring>
#include <vector>
#include <cstdio>
using namespace std;
//***********************************************************************************************
const int maxnode = 10000 * 50 + 10;
const int sigma_size = 26;
// 字母表为全体小写字母的Trie
struct Trie {
int ch[maxnode][sigma_size];
int val[maxnode];
int sz; // 结点总数
void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } // 初始时只有一个根结点
int idx(char c) { return c - 'a'; } // 字符c的编号
// 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点”
void insert(const char *s, int v) {
int u = 0, n = strlen(s);
for(int i = 0; i < n; i++) {
int c = idx(s[i]);
if(!ch[u][c]) { // 结点不存在
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0; // 中间结点的附加信息为0
ch[u][c] = sz++; // 新建结点
}
u = ch[u][c]; // 往下走
}
val[u] = v; // 字符串的最后一个字符的附加信息为v
}
};
//*****************************************************************************************************
//以下为模板测试
Trie trie;
char s[100];
int main()
{
int n;
while(scanf("%d",&n)!=EOF){
trie.clear();
int maxv=0;
for(int i=1;i<=n;i++){
scanf("%s",s);
int len=strlen(s);
maxv=max(maxv,len);
trie.insert(s,1);
}
printf("%d\n",(trie.sz-1)*2-maxv+n);
}
return 0;
}
/*
3
abcded
abcdehi
abcdhijk
*/
方法二:
总的单词长度加起来即为sum,构造好字典树之后利用栈进行dfs,如果对于字典树中的结点它的v大于1(证明不止一个单词经过这个结点),答案减去(v-1)*2,最后再减去最长单词的长度(因为最后一个单词肯定只需要访问一次)+n
#include <cstring>
#include <vector>
#include <cstdio>
#include<stack>
using namespace std;
const int maxnode = 10000 * 50 + 10;
const int sigma_size = 26;
char s[110],s1[110];
stack<int>st;
// 字母表为全体小写字母的Trie
struct Trie {
int ch[maxnode][sigma_size];
int val[maxnode];
int sz; // 结点总数
void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } // 初始时只有一个根结点
int idx(char c) { return c - 'a'; } // 字符c的编号
void insert(const char *s, int v) {
int u = 0, n = strlen(s);
for(int i = 0; i < n; i++) {
int c = idx(s[i]);
if(!ch[u][c]) { // 结点不存在
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz]=0;
ch[u][c] = sz++; // 新建结点
}
u = ch[u][c]; // 往下走
val[u]+=v;
}
}
int dfs(int v){
int sum=0;
while(!st.empty())
st.pop();
st.push(v);
while(!st.empty()){
int u=st.top();
st.pop();
if(u!=0)
sum+=2*(val[u]-1);
for(int i=0;i<26;i++){
if(ch[u][i]!=0)
st.push(ch[u][i]);
}
}
return sum;
}
};
Trie trie;
int main()
{
int n;
while(scanf("%d",&n)!=EOF){
trie.clear();
int maxv=0,sum=0;
for(int i=1;i<=n;i++){
scanf("%s",s);
int len=strlen(s);
sum+=len;
maxv=max(maxv,len);
trie.insert(s,1);
}
printf("%d\n",sum*2-trie.dfs(0)-maxv+n);
}
return 0;
}