#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-9;
const int MAXN = 128;
pair<int, int> best_choice[MAXN];
vector<string> query(int f, int t) {
  cout << "? " << f << " " << t << endl << flush;
  int R = t - f + 1;
  assert(R & 1);
  vector<vector<string> > substrings(R);
  for (int i = (0); i < ((R * (R + 1)) / 2); i++) {
    string tmp;
    cin >> tmp;
    substrings[int((tmp).size()) - 1].push_back(tmp);
  }
  for (int r = (0); r < (R); r++) assert(int((substrings[r]).size()) == R - r);
  unordered_map<char, int> mem_num;
  for (auto it : substrings[0]) mem_num[it[0]] += 1;
  vector<string> result;
  for (int r = (1); r < (R); r++) {
    if (R < r * 2 + 1) break;
    unordered_map<char, int> target;
    for (auto it : mem_num) target[it.first] += it.second * (r + 1);
    for (int i = (0); i < (r - 1); i++)
      for (auto c : result[i]) target[c] += i + 1;
    for (auto str : substrings[r])
      for (auto c : str) target[c] -= 1;
    result.push_back("");
    for (auto it : target) {
      int v = it.second;
      if (v < 0) {
        cerr << "mem_num" << endl;
        for (auto it2 : mem_num) cerr << it2.first << " " << it2.second << endl;
        cerr << "target" << endl;
        for (auto it2 : target) cerr << it2.first << " " << it2.second << endl;
      }
      assert(v >= 0);
      while (v-- > 0) {
        result[r - 1] += it.first;
        mem_num[it.first] -= 1;
      }
    }
    assert(int((result[r - 1]).size()) == 2);
  }
  string lastpart = "";
  for (auto it : mem_num) {
    int v = it.second;
    assert(v >= 0);
    while (v-- > 0) {
      lastpart += it.first;
    }
  }
  assert(int((lastpart).size()) == 1);
  result.push_back(lastpart);
  cerr << "Q " << f << " " << t << endl;
  for (auto it : result) cerr << it << endl;
  return result;
}
string subtask(int N) {
  assert(N & 1);
  int H = N / 2;
  vector<string> words1 = query(1, N);
  vector<string> words2 = query(2, N + 1);
  assert(H + 1 == int((words1).size()) && H + 1 == int((words2).size()));
  string res(N + 1, '#');
  res[H] = words1[H][0];
  res[H + 1] = words2[H][0];
  for (int i = int(H) - 1; i >= (0); i--) {
    res[i] = ((words1[i][0] == res[2 * H - i]) ? words1[i][1] : words1[i][0]);
    res[2 * H - i + 1] =
        ((words2[i][0] == res[i + 1]) ? words2[i][1] : words2[i][0]);
  }
  cerr << "subtask:" << res << endl;
  return res;
}
int main() {
  for (int N = (1); N < (MAXN); N++) {
    int allowed = (int)((N + 1) * (N + 1) * 0.777);
    int bestk = -1;
    int bestrem = -1;
    int best = allowed + 1;
    for (int k = (0); k < (N); k++) {
      int c = k * 2 + 1;
      if (c >= N) break;
      int current = c * (c + 1);
      int rem = (N - c - 1) * 2 - 1;
      if (c == N - 1) rem = 0;
      assert(rem >= 0);
      if (rem > N) continue;
      current += (rem * (rem + 1)) / 2;
      if (current < best) {
        best = current;
        bestk = c;
        bestrem = rem;
      }
    }
    best_choice[N] = make_pair(bestk, bestrem);
  }
  int N;
  cin >> N;
  string res(N, '#');
  if (N <= 3) {
    for (int i = (0); i < (N); i++) {
      cout << "? " << i + 1 << " " << i + 1 << endl << flush;
      string tmp;
      cin >> tmp;
      res[i] = tmp[0];
    }
    cout << "! " << res << endl;
    return 0;
  }
  string part = subtask(best_choice[N].first);
  int rem = best_choice[N].second;
  assert(rem > 0);
  auto words = query(N - rem + 1, N);
  assert(int((words).size()) + int((part).size()) == N);
  for (int i = (0); i < (int((part).size())); i++) res[i] = part[i];
  res[int((part).size())] = words[int((words).size()) - 1][0];
  for (int i = (0); i < (int((words).size()) - 1); i++) {
    res[N - i - 1] = ((res[N - int((words).size()) * 2 + 1 + i] == words[i][0])
                          ? words[i][1]
                          : words[i][0]);
  }
  cout << "! " << res << endl;
  return 0;
}
