#include <iostream>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <vector>
#include <valarray>
#include <array>
#include <queue>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <algorithm>
#include <cmath>
#include <complex>
#include <random>

using namespace std;
typedef long long ll;

struct NTree {
	// syakyou simasita
	// demo TLE sisou...
	struct Node;
	typedef char D;
	typedef Node* NP;
	static Node last_d;
	static NP last;
	struct Node {
		NP l, r;
		int sz;
		D v;
		Node(D v) :l(last), r(last), sz(1), v(v) {}
		Node() :l(nullptr), r(nullptr), sz(0) {}

		NP update() {
			sz = 1+l->sz+r->sz;
			return this;
		}

		void set(int a, D x) {
			if (a < l->sz) {
				l->set(a, x);
			} else if (a == l->sz) {
				v = x;
			} else {
				r->set(a - (l->sz + 1), x);
			}
		}
		void allget(D d[]) {
			if (!sz) return;
			l->allget(d);
			d[l->sz] = v;
			r->allget(d+l->sz+1);
		}
	} *n;

	static uint xor128() {
		static uint x = 123456789, y = 362436039, z = 525188629, w = 88675123;
		uint t;
		t = (x^(x<<11)); x = y; y = z; z = w; return (w = (w^(w>>19))^(t^(t>>8)));
	}

	static NP merge(NP l, NP r) {
		if (!l->sz) return r;
		if (!r->sz) return l;
		if ((int)(xor128() % (l->sz + r->sz)) < l->sz) {
			l->r = merge(l->r, r);
			return l->update();
		} else {
			r->l = merge(l, r->l);
			return r->update();
		}
	}

	static pair<NP, NP> split(NP x, int k) {
		if (!x->sz) return {last, last};
		if (k <= x->l->sz) {
			auto y = split(x->l, k);
			x->l = y.second;
			y.second = x->update();
			return y;
		} else {
			auto y = split(x->r, k- x->l->sz - 1);
			x->r = y.first;
			y.first = x->update();
			return y;
		}
	}

	static NP built(int sz, D d[]) {
		if (!sz) return last;
		int md = sz/2;
		NP x = new Node(d[md]);
		x->l = built(md, d);
		x->r = built(sz-(md+1), d+(md+1));
		return x->update();
	}
	NTree() : n(last){}
	NTree(NP n) : n(n) {}
	NTree(D d) : n(new Node(d)) {}
	NTree(int sz, D d[]) {
		n = built(sz, d);
	}
	void merge(NTree r) {
		n = merge(n, r.n);
	}

	NTree split(int k) {
		auto u = split(n, k);
		n = u.first;
		return NTree(u.second);
	}
	void set(int a, D x) {
		n->set(a, x);
	}
	void allget(D d[]) {
		n->allget(d);
		d[n->sz] = '\0';
	}
};
NTree::Node NTree::last_d = NTree::Node();
NTree::NP NTree::last = &last_d;

const int MN = 100100;
const int MS = 2000100;
NTree tr[MN];
char buf[MS];

void deb(NTree t) {
	t.allget(buf);
	printf("d %s\n", buf);
}
int main() {
	int n, q;
	scanf("%d %d", &n, &q);
	for (int i = 0; i < n; i++) {
		scanf(" %s", buf);
		tr[i] = NTree(strlen(buf), buf);
	}

	for (int i = 0; i < q; i++) {
		int ty;
		scanf("%d", &ty);
//		printf("quey %d %d\n", i, ty);
		if (ty == 1) {
			int a, b, c, d;
			scanf("%d %d %d %d", &a, &b, &c, &d); a--; b--; c--; d--;
//			printf("%d %d %d %d\n", a, b, c, d);
//			deb(tr[a]);
//			deb(tr[c]);
			auto t1 = tr[a].split(b);
			auto t2 = tr[c].split(d);
//			deb(t1);
//			deb(t2);
			tr[a].merge(t2);
			tr[c].merge(t1);
//			deb(tr[a]);
//			deb(tr[c]);
		} else {
			int a, b; char c;
			scanf("%d %d %c", &a, &b, &c); a--; b--;
//			printf("type 2 %d %d %d\n", a, b, tr[a].n->sz);
			tr[a].set(b, c);
		}
		for (int i = 0; i < n; i++) {
//			tr[i].allget(buf);
//			printf("%s\n", buf);
		}
//		printf("\n");
//		printf("\n");
	}
	for (int i = 0; i < n; i++) {
		tr[i].allget(buf);
		printf("%s\n", buf);
	}
    return 0;
}

Measured memory_bytes: 66120