#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <utility>
#include <cctype>
#include <numeric>
using namespace std;

#define rep(i,n) for(int (i)=0; (i)<(int)(n); ++(i))
#define foreach(c,i) for(__typeof((c).begin()) i=(c).begin();i!=(c).end();i++)

vector<string> s;
string buf = "";
int ci = 0, cj = 0;

void input() {
  string l;
  while (1) {
    getline(cin, l);
    if (l == "END_OF_TEXT") {
      break;
    }
    s.push_back(l);
  }
}

// カーソルを現在の行の先頭文字に移動する
void op_a() { cj = 0; }

// カーソルを現在の行の行末に移動する
void op_e() { cj = s[ci].size(); }

// 上に行があれば、カーソルを上の行の先頭文字に移動する。
// 上に行がなければ、カーソルを先頭文字に移動する。
void op_p() {
  if (ci != 0) 
    --ci;
  op_a();
}

// 下に行があれば、カーソルを下の行の先頭文字に移動する。
// 下に行がなければ、カーソルを先頭文字に移動する。
void op_n() {
  if (ci != s.size() - 1) 
    ++ci;
  op_a();
}

// カーソルが行末にない場合、カーソルを１つ右に移動する 
// カーソルが行末にありかつ下に行がある場合は、
// カーソルを下の行の先頭文字へ移動する 
// それ以外の場合は何もしない
void op_f() {
  if (cj != s[ci].size()) {
    ++cj;
  } else if (cj == s[ci].size() && ci != s.size() - 1) {
    op_n();
  }
}

// カーソルが先頭文字でないならば、カーソルを１つ左に移動する。
// カーソルが先頭文字でありかつ上に行がある場合は、
// カーソルを上の行の行末へ移動する。
// それ以外の場合は何もしない。
void op_b() {
  if (cj != 0) {
    --cj;
  } else if (cj == 0 && ci != 0) {
    --ci;
    op_e();
  }
}

// カーソルが文字を指す場合はその文字を削除する。
// 削除された文字の右側の文字列は左にシフトされる。
// カーソルが行末を指し下に行がある場合は、
// 下の行をそのままカーソルの位置に繋げる。以下の行は上にシフトされる。
// それ以外の場合は何もしない。
void op_d() {
  if (cj != s[ci].size()) {
    // prev
    // s[ci].erase(cj, 1);
    s[ci] = s[ci].substr(0, cj) + s[ci].substr(cj + 1);
  } else if (cj == s[ci].size() && ci != s.size() - 1) {
    // prev
    // s[ci].erase(cj, 1);
    // s[ci] += s[ci+1];
    // s.erase(s.begin() + ci + 1);
    s[ci] += s[ci+1];
    s.erase(s.begin() + ci + 1);
  }
}

// カーソルが行末にある場合、下に行があれば、
// d と同じ操作をしバッファに１つの改行を記録する。
// カーソルが行末にない場合、カーソルが指す文字を含めた
// 右側すべての文字を切り取りそれをバッファに記録する。
// カーソルは元の行の行末を指すようになる。全ての記録においてバッファは上書きされる。
// それ以外の場合は何もしない。(clarification at 14:20)
void op_k() {
  if (cj == s[ci].size() && ci != s.size() - 1) {
    // prev
    // s[ci].erase(cj, 1);
    // s[ci] += s[ci+1];
    // s.erase(s.begin() + ci + 1);
    s[ci] += s[ci+1];
    s.erase(s.begin() + ci + 1);
    buf = "\n";
  } else  {
    // prev
    // buf = s[ci].substr(cj);
    // s[ci].erase(cj);
    buf = s[ci].substr(cj);
    s[ci] = s[ci].substr(0, cj);
  }
}

// バッファが空の場合はなにもしない。
// バッファの内容が改行の場合、カーソルが指す文字の直前で改行を行う。
// カーソルは新しい行の先頭文字へ移動する。
// バッファの内容が文字列の場合、カーソルの位置にバッファの内容を挿入する。
// カーソルの位置はもともと指していた文字または行末の場所へ移動する。 
void op_y() {
  if (buf == "") return;
  if (buf == "\n") {
    // prev
    // inserter(s, s.begin() + ci + 1) = s[ci].substr(cj);
    // s[ci].erase(cj);
    // ++ci; cj = 0;
    vector<string>::iterator itr = s.begin();
    rep(i,ci+1) ++itr;
    s.insert(itr, s[ci].substr(cj));
    s[ci] = s[ci].substr(0, cj);
    ++ci; cj = 0;
  } else {
    // prev
    // s[ci].insert(cj, buf.c_str());
    // cj += buf.size();
    s[ci] = s[ci].substr(0, cj) + buf + s[ci].substr(cj);
    cj += buf.size();
  }
}

void interpret() {
  string op;
  while (1) {
    getline(cin, op);
    if (op == "-") break;
    if (op == "a") {
      op_a();
    } else if (op == "e") {
      op_e();
    } else if (op == "p") {
      op_p();
    } else if (op == "n") {
      op_n();
    } else if (op == "f") {
      op_f();
    } else if (op == "b") {
      op_b();
    } else if (op == "d") {
      op_d();
    } else if (op == "k") {
      op_k();
    } else if (op == "y") {
      op_y();
    }
  }
  rep(i,s.size()) {
    // printf("%s\n", s[i].c_str());
    cout << s[i] << endl;
  }
}

int main() {
  input();
  interpret();
  return 0;
}

Measured memory_bytes: 1200