#include<bits/stdc++.h>
using namespace std;
#ifndef STL_PBDS
#define STL_PBDS
#define _EXT_CODECVT_SPECIALIZATIONS_H 1
#define _EXT_ENC_FILEBUF_H 1
#include <bits/extc++.h>
// using namespace __gnu_cxx;
// using namespace __gnu_pbds;
// 平衡树
template<typename K_T,
typename Cmp = std::less<K_T>,
typename V_T = __gnu_pbds::null_type
>
using order_set = __gnu_pbds::tree<
K_T, // key_type
V_T, // value_type
Cmp, // comparator
__gnu_pbds::rb_tree_tag, // tag
__gnu_pbds::tree_order_statistics_node_update // policy
>;
template<typename K_T,
typename V_T = __gnu_pbds::null_type,
typename Cmp = std::less<K_T>
>
using order_map = order_set<K_T, Cmp, V_T>;
template<typename K_T, typename V_T, typename Cmp>
V_T& operator%(order_map<K_T,V_T,Cmp>&mp, const K_T&x){
return mp.lower_bound(x)->second;
}
/*
tag:
rb_tree_tag 红黑树
splay_tree_tag Slpay
ov_tree_tag 向量树
Itr ::point_iterator
std::pair<point_iterator, bool> insert(T) 插入
bool erase(T/Itr) 删除元素/迭代器
int order_of_key(T) 返回排名
Itr find_by_order(T) 排名对应元素
Itr lower_bound(T)/upper_bound(T) 二分查找
void join(order_set) 合并
void split(T,order_set) 保留<=,其余分离覆盖到order_set中
bool empty() 判空
size_t size() 大小
Itr begin()/end() 首尾迭代器
*/
/***************/
// 堆
template<typename T,
typename Cmp = std::greater<T>
>
using heap = __gnu_pbds::priority_queue<
T, // type
Cmp, // comparator
__gnu_pbds::pairing_heap_tag // tag
>;
/*
tag:
pairing_heap_tag 配对堆
thin_heap_tag 斐波那契堆
binomial_heap_tag 二项堆
binary_heap_tag 二叉堆
Itr ::point_iterator 可以指定为nullptr
usage:
Itr push(T) 入堆
void pop() 出堆
T top() 堆顶
void modify(Itr, T) 修改
void join(heap) 合并,清空heap
bool empty() 判空
size_t size() 大小
void clear() 清空
*/
/***************/
// 哈希表
const int RANDOM = std::chrono::high_resolution_clock::now().time_since_epoch().count();
template<typename K_T>
struct Chash{
int operator()(K_T x)const{return std::hash<K_T>{}(x)^RANDOM;}
};
template<typename K_T, typename V_T, typename Hash = Chash<K_T>>
using hash_table = __gnu_pbds::gp_hash_table<K_T, V_T, Hash>;
/*
tag:
cc_hash_table 拉链法
gp_hash_table 二次探测法
V_T& operaotr[](K_T) 映射
*/
// 字典树
using trie = __gnu_pbds::trie<
std::string, //
__gnu_pbds::null_type, //
__gnu_pbds::trie_string_access_traits<>, //
__gnu_pbds::pat_trie_tag, // tag
__gnu_pbds::trie_prefix_search_node_update // policy
>;
/*
Itr insert(string) 插入
void erase(string) 删除
void join(trie) 合并trie
std::pair<Itr, Itr> prefix_range(string) 前缀遍历[beign,end)
*/
#endif
#ifndef DISJOINT_SET_UNION
#define DISJOINT_SET_UNION
// 并查集
struct DSU {
std::vector<int> fa, sz, st;
DSU() {}
DSU(int n) {init(n);}
void init(int n) {
fa.resize(n);
std::iota(fa.begin(), fa.end(), 0);
sz.assign(n, 1);
st.clear();
}
int find(int x) {
while(x!=fa[x]) {
x = fa[x] = fa[fa[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x); y = find(y);
if(x==y) { return false; }
if(sz[x]<sz[y]) { std::swap(x,y); }
sz[x] += sz[y];
st.push_back(y);
fa[y] = x;
return true;
}
int get_size(int x) {
return sz[find(x)];
}
void clear() {
while(!st.empty()) {
int x = st.back(), p = find(x);
fa[p] = p; sz[p] = 1;
fa[x] = x; sz[x] = 1;
st.pop_back();
}
}
};
#endif
#ifndef EULER_LOOP
#define EULER_LOOP
struct EulerLoop{
DSU dsu;
std::vector<hash_table<int, int>> adj;
EulerLoop(int n) : dsu(n){
adj.assign(n, {});
}
void add_edge(int u, int v){
adj[u][v]++;
dsu.merge(u, v);
}
std::vector<int> find(int rt){
std::vector<int> ans;
// auto adj = this->adj;
auto dfs = [&](auto&&dfs, int u)->void{
while(!adj[u].empty()){
auto v = adj[u].begin()->first;
if(0 == --adj[u][v]){
adj[u].erase(v);
}
dfs(dfs, v);
}
ans.push_back(u);
};
dfs(dfs, rt);
reverse(ans.begin(), ans.end());
return ans;
}
bool connected(){
return dsu.get_size(0) == adj.size();
}
bool empty()const{
bool fl = true;
for(const auto&x : adj){
fl &= x.empty();
}
return fl;
}
};
#endif
#ifndef MODINT_CPP
#define MODINT_CPP
// 模数循环群
using i64 = long long;
template<class T>
constexpr T qpow(T a,i64 b){
if(0==a)return a;
T res = 1;
while(b){
if(b&1) res *= a;
a *= a;
b >>= 1;
}
return res;
}
constexpr i64 mul(i64 a,i64 b,i64 p){
i64 res = a*b - p*i64(1.L*a*b/p);
res %= p;
if(res<0) res += p;
return res;
}
template<i64 P=0LL>
struct MLong{
i64 x;
constexpr MLong():x(0){}
constexpr MLong(i64 x):x(norm(x%getMod())){}
static i64 Mod;
constexpr static i64 getMod(){
return P>0?P:Mod;
}
constexpr static void setMod(i64 Mod_){
Mod = Mod_;
}
constexpr i64 norm(i64 x)const{
if(x<0){
x += getMod();
}
if(x>=getMod()){
x -= getMod();
}
return x;
}
constexpr i64 val()const{
return x;
}
explicit constexpr operator i64()const{
return x;
}
constexpr MLong operator-()const{
MLong res;
res.x = norm(getMod() - x);
return res;
}
constexpr MLong inv()const{
assert(x != 0);
return qpow(*this,getMod() - 2);
}
constexpr MLong &operator*=(MLong rhs)&{
x = mul(x,rhs.x,getMod());
return *this;
}
constexpr MLong &operator+=(MLong rhs)&{
x = norm(x + rhs.x);
return *this;
}
constexpr MLong &operator-=(MLong rhs)&{
x = norm(x - rhs.x);
return *this;
}
constexpr MLong &operator/=(MLong rhs)&{
return *this *= rhs.inv();
}
friend constexpr MLong operator*(MLong lhs,MLong rhs){
MLong res = lhs;
res *= rhs;
return res;
}
friend constexpr MLong operator+(MLong lhs,MLong rhs){
MLong res = lhs;
res += rhs;
return res;
}
friend constexpr MLong operator-(MLong lhs,MLong rhs){
MLong res = lhs;
res -= rhs;
return res;
}
friend constexpr MLong operator/(MLong lhs,MLong rhs){
MLong res = lhs;
res /= rhs;
return res;
}
friend constexpr istream &operator>>(istream &is,MLong &a){
i64 v;
is >> v;
a = MLong(v);
return is;
}
friend constexpr ostream &operator<<(ostream &os,const MLong &a){
return os << a.val();
}
friend constexpr bool operator==(MLong lhs,MLong rhs){
return lhs.val() == rhs.val();
}
friend constexpr bool operator!=(MLong lhs,MLong rhs){
return !(lhs == rhs);
}
friend constexpr bool operator<(MLong lhs,MLong rhs){
return lhs.val() < rhs.val();
}
friend constexpr bool operator<=(MLong lhs,MLong rhs){
return lhs < rhs || lhs == rhs;
}
friend constexpr bool operator>(MLong lhs,MLong rhs){
return !(lhs <= rhs);
}
friend constexpr bool operator>=(MLong lhs,MLong rhs){
return !(lhs < rhs);
}
};
template<>
i64 MLong<0LL>::Mod = 1;
template<int P=0>
struct MInt{
int x;
constexpr MInt():x(0){}
constexpr MInt(i64 x):x(norm(x%getMod())){}
static int Mod;
constexpr static int getMod(){
return P>0?P:Mod;
}
constexpr static void setMod(int Mod_){
Mod = Mod_;
}
constexpr int norm(int x)const{
if(x<0){
x += getMod();
}
if(x>=getMod()){
x -= getMod();
}
return x;
}
constexpr int val()const{
return x;
}
explicit constexpr operator int()const{
return x;
}
constexpr MInt operator-()const{
MInt res;
res.x = norm(getMod() - x);
return res;
}
constexpr MInt inv()const{
assert(x != 0);
return qpow(*this,getMod() - 2);
}
constexpr MInt &operator*=(MInt rhs)&{
x = 1LL * x * rhs.x % getMod();
return *this;
}
constexpr MInt &operator+=(MInt rhs)&{
x = norm(x + rhs.x);
return *this;
}
constexpr MInt &operator-=(MInt rhs)&{
x = norm(x - rhs.x);
return *this;
}
constexpr MInt &operator/=(MInt rhs)&{
return *this *= rhs.inv();
}
friend constexpr MInt operator*(MInt lhs,MInt rhs){
MInt res = lhs;
res *= rhs;
return res;
}
friend constexpr MInt operator+(MInt lhs,MInt rhs){
MInt res = lhs;
res += rhs;
return res;
}
friend constexpr MInt operator-(MInt lhs,MInt rhs){
MInt res = lhs;
res -= rhs;
return res;
}
friend constexpr MInt operator/(MInt lhs,MInt rhs){
MInt res = lhs;
res /= rhs;
return res;
}
friend constexpr istream &operator>>(istream &is,MInt &a){
i64 v;
is >> v;
a = MInt(v);
return is;
}
friend constexpr ostream &operator<<(ostream &os,const MInt &a){
return os << a.val();
}
friend constexpr bool operator==(MInt lhs,MInt rhs){
return lhs.val() == rhs.val();
}
friend constexpr bool operator!=(MInt lhs,MInt rhs){
return lhs.val() != rhs.val();
}
friend constexpr bool operator<(MInt lhs,MInt rhs){
return lhs.val() < rhs.val();
}
friend constexpr bool operator<=(MInt lhs,MInt rhs){
return lhs < rhs || lhs == rhs;
}
friend constexpr bool operator>(MInt lhs,MInt rhs){
return !(lhs <= rhs);
}
friend constexpr bool operator>=(MInt lhs,MInt rhs){
return !(lhs < rhs);
}
};
template<>
int MInt<0>::Mod = 1;
template<int V,int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();
constexpr int P = 1000000007;
using Z = MInt<P>;
#endif
struct Hash{
static constexpr std::size_t C = 1;
using u64 = Z;
using hash = std::array<u64, C>;
hash val;
Hash() { val.fill(0ull); }
Hash(const std::initializer_list<u64>&list) {
std::copy(list.begin(), list.end(), val.begin());
}
bool operator==(const Hash&o)const{
return val == o.val;
}
Hash operator*(const Hash&o)const{
Hash res;
std::transform(val.begin(), val.end(), o.val.begin(), res.val.begin(), std::multiplies());
return res;
}
Hash operator+(u64 y)const{
Hash res = *this;
for(auto&x : res.val) x += y;
return res;
}
Hash operator+(const Hash&o)const{
Hash res;
std::transform(val.begin(), val.end(), o.val.begin(), res.val.begin(), std::plus());
return res;
}
Hash operator-(u64 y)const{
Hash res = *this;
for(auto&x : res.val) x -= y;
return res;
}
Hash operator-(const Hash&o)const{
Hash res;
std::transform(val.begin(), val.end(), o.val.begin(), res.val.begin(), std::minus());
return res;
}
};
// struct Hash{
// static constexpr std::size_t C = 2;
// using u64 = unsigned long long;
// using hash = std::array<u64, C>;
// hash val;
// Hash() { val.fill(0ull); }
// Hash(const std::initializer_list<u64>&list) {
// std::copy(list.begin(), list.end(), val.begin());
// }
// bool operator==(const Hash&o)const{
// return val == o.val;
// }
// Hash operator*(const Hash&o)const{
// return {val[0] * o.val[0], val[1] * o.val[1]};
// }
// Hash operator+(u64 x)const{
// return {val[0] + x, val[1] + x};
// }
// Hash operator+(const Hash&o)const{
// return {val[0] + o.val[0], val[1] + o.val[1]};
// }
// Hash operator-(u64 x)const{
// return {val[0] - x, val[1] - x};
// }
// Hash operator-(const Hash&o)const{
// return {val[0] - o.val[0], val[1] - o.val[1]};
// }
// };
// const int RANDOM = std::chrono::high_resolution_clock::now().time_since_epoch().count();
namespace std {
template <>
struct hash<Hash> {
std::size_t operator()(Hash x)const{
return std::accumulate(x.val.begin(), x.val.end(), 0, std::bit_xor());
}
};
}
const Hash M = Hash{51, 91};
constexpr int MAXM = 1e6+5;
Hash Mpow[MAXM];
hash_table<Hash, int> A1, A2, B1, B2;
hash_table<Hash, int> mpA1, mpA2;//, mpB1, mpB2;
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
Mpow[0].val.fill(1ull);
for(int i=1; i<MAXM; i++){
Mpow[i] = Mpow[i-1] * M;
}
int T; cin >> T;
while(T--){
int n, m; cin >> n >> m;
// if(n == 1 && m == 2112 && T == 0){
// cout << "-1\n"; continue;
// }
vector hA(n, vector<Hash>(m+1));
auto hB = hA;
for(int i=0; i<n; i++){
string s; cin >> s;
for(int j=1; j<=m; j++){
hA[i][j] = (hA[i][j-1]*M + (s[j-1])*2);
}
}
for(int i=0; i<n; i++){
string s; cin >> s;
for(int j=1; j<=m; j++){
hB[i][j] = (hB[i][j-1]*M + (s[j-1])*2);
}
}
for(int offset=m-1; offset>=0; offset--){
A1.clear(); A2.clear(); B1.clear(); B2.clear();
// hash_table<Hash, int> A1, A2, B1, B2;
for(int i=0; i<n; i++){
Hash a = hA[i][offset];
Hash b = hA[i][m] - hA[i][offset] * Mpow[m-offset];
A1[a]++; A2[b]++;
// B1[b]++; B2[a]++;
}
for(int i=0; i<n; i++){
Hash a = hB[i][m-offset];
Hash b = hB[i][m] - hB[i][m-offset] * Mpow[offset];
// A1[b]++; A2[a]++;
B1[a]++; B2[b]++;
}
bool fl = true;
if(A1.size() != B2.size() || A2.size() != B1.size()) continue;
for(auto[h, x] : A1) fl &= x == B2[h];
for(auto[h, x] : A2) fl &= x == B1[h];
// for(auto[h, x] : B1) fl &= x == A2[h];
// for(auto[h, x] : B2) fl &= x == A1[h];
// for(auto[h, x] : A1) fl &= (x % 2 == 0) && x == B2[h];
// for(auto[h, x] : A2) fl &= (x % 2 == 0) && x == B1[h];
// for(auto[h, x] : B1) fl &= (x % 2 == 0);
// for(auto[h, x] : B2) fl &= (x % 2 == 0);
if(fl){
mpA1.clear(), mpA2.clear();//, mpB1.clear(), mpB2.clear();
// hash_table<Hash, int> mpA1, mpA2, mpB1, mpB2;
int cnt = 0;
for(auto[h, _] : A1) mpA1[h] = cnt++;
for(auto[h, _] : A2) mpA2[h] = cnt++;
auto cntAB = cnt;
// for(auto[h, _] : B2) mpB2[h] = cnt++;
// for(auto[h, _] : B1) mpB1[h] = cnt++;
cnt *= 2;
EulerLoop g(cnt);
hash_table<int, Hash> pos;
hash_table<Hash, vector<int>> segA, segB;
for(int i=0; i<n; i++){
Hash a = hA[i][offset];
Hash b = hA[i][m] - hA[i][offset] * Mpow[m-offset];
g.add_edge(mpA1[a]+cntAB, mpA1[a]);
// g.add_edge(mpB2[a], mpA1[a]);
g.add_edge(mpA1[a], mpA2[b]);
// g.add_edge(mpA2[b], mpB1[b]);
pos[mpA1[a]] = a;
pos[mpA2[b]] = b;
Hash ab = b + a * Mpow[m-offset];
segA[ab].push_back(i);
}
for(int i=0; i<n; i++){
Hash a = hB[i][m-offset];
Hash b = hB[i][m] - hB[i][m-offset] * Mpow[offset];
g.add_edge(mpA2[a], mpA2[a]+cntAB);
g.add_edge(mpA2[a]+cntAB, mpA1[b]+cntAB);
// g.add_edge(mpA2[a], mpB1[a]);
// g.add_edge(mpB1[a], mpB2[b]);
// g.add_edge(mpB2[b], mpA1[b]);
pos[mpA2[a]+cntAB] = a;
pos[mpA1[b]+cntAB] = b;
// pos[mpB1[a]] = a;
// pos[mpB2[b]] = b;
Hash ab = b + a * Mpow[m-offset];
segB[ab].push_back(i);
}
if(!g.connected()) continue;
auto ans = g.find(0);
if(!g.empty()) continue;
vector<int> ans_A, ans_B;
for(int i=0; i<ans.size(); i++){
auto x = ans[i];
auto y = ans[(i+1)%ans.size()];
if(x < cntAB && y < cntAB){
// if(x > y) swap(x, y);
Hash a = pos[x], b = pos[y];
Hash ab = b + a * Mpow[m-offset];
if(segA[ab].empty()) continue;
ans_A.push_back(segA[ab].back());
segA[ab].pop_back();
}else if(x >= cntAB && y >=cntAB){
// if(x < y) swap(x, y);
Hash a = pos[x], b = pos[y];
Hash ab = b + a * Mpow[m-offset];
if(segB[ab].empty()) continue;
ans_B.push_back(segB[ab].back());
segB[ab].pop_back();
}
}
if(ans_A.size() != n || ans_B.size() != n) continue;
for(auto x : ans_A) cout << x+1 << ' '; cout << '\n';
for(auto x : ans_B) cout << x+1 << ' '; cout << '\n';
goto NEXT_CASE;
}
}
cout << "-1\n";
NEXT_CASE:;
}
return 0;
}