//package main;
import java.io.*;
import java.util.*;
import java.lang.*;
import java.math.*;
public class Submission {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter (System.out);
        //BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt")));
    	StringTokenizer st = new StringTokenizer(reader.readLine()," ");
    	int answered=0;
    	int n = Integer.parseInt(st.nextToken());
    	if (n>3) {
    	int[] vals = new int[26];
    	int[] answer= new int[n];
    	for (int i=0;i<n;i++) answer[i]=-1;
    	int[][] instancesOfSize = new int[26][n+1];
    	out.println("? 1 "+n);
    	out.flush();
    	for (int i=0;i<n*(n+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()==1) vals[s.charAt(0)-'a']++;
    		if (s.length()<=(n+1)/2)
    			for (int j=0;j<s.length();j++)
    				instancesOfSize[s.charAt(j)-'a'][s.length()]++;
    	}
    	int secGoSize = (n+1)/2;
    	int[][] secGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? 1 "+secGoSize);
    	out.flush();
    	for (int i=0;i<secGoSize*(secGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(secGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				secGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int thdGoSize = (n-1)/2;
    	int[][] thdGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? "+(n/2+1)+" "+(thdGoSize+(n/2+1)-1));
    	out.flush();
    	for (int i=0;i<thdGoSize*(thdGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(thdGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				thdGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int last=0;
    	for (int i=0;i<26;i++)
    		if (vals[i]==1) last=i;
    	for (int i=0;i<n;i++)
    		instancesOfSize[last][i+1]--;

    	//first double-ifier
    	List<List<Integer>> fstDoubles = new ArrayList<>();
    	for (int i=0;i<n+1;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(n+1)/2;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(n+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			//if (j==1) System.out.println(instancesOfSize[j][i]+" "+i);
    			if (instancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    			if (instancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (instancesOfSize[i][(n+1)/2]!=0) {
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    		if (instancesOfSize[i][(n+1)/2]!=0) {//again!
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    	}
    	answer[n-1]=last;
    	int first = fstDoubles.get(1).get(0);
    	fstDoubles.get(1).remove(0);
    	answer[0]=first;
    	answered+=2;
    	//for (int i=0;i<fstDoubles.get((n+1)/2).size();i++) System.out.println(fstDoubles.get((n+1)/2).get(i));

    	//second doubles run
    	for (int i=0;i<=secGoSize;i++)
    		secGoInstancesOfSize[first][i+1]--;
    	List<List<Integer>> secDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) secDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(secGoSize+1)/2;i++) secDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(secGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (secGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (secGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {//again!
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    	}
    	int interim = secDoubles.get(1).get(0);
    	secDoubles.get(1).remove(0);
    	answer[secGoSize-1]=interim;
    	answered++;
    	//System.out.println(fstDoubles.get((n+1)/2).get(0));
    	fstDoubles.get((n+1)/2).remove(fstDoubles.get((n+1)/2).indexOf(interim));
    	if (n%2==0) {
        	interim=fstDoubles.get((n+1)/2).get(0);
        	fstDoubles.get((n+1)/2).remove(0);
        	answer[secGoSize]=interim;
        	answered++;
    	}

    	//THIRD doubles run
    	for (int i=0;i<=thdGoSize;i++)
    		thdGoInstancesOfSize[interim][i+1]--;
    	List<List<Integer>> thdDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(thdGoSize+1)/2;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(thdGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (thdGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (thdGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {//again!
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    	}
    	//WE DID ITTT
    	/*System.out.println("hh");
    	for (int i=0;i<fstDoubles.size();i++) {
    		if (fstDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<fstDoubles.get(i).size();j++) System.out.print(fstDoubles.get(i).get(j)+" ");
    		if (fstDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<secDoubles.size();i++) {
    		if (secDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<secDoubles.get(i).size();j++) System.out.print(secDoubles.get(i).get(j)+" ");
    		if (secDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<thdDoubles.size();i++) {
    		if (thdDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<thdDoubles.get(i).size();j++) System.out.print(thdDoubles.get(i).get(j)+" ");
    		if (thdDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();*/
    	//System.out.println(answered);
    	while (answered<n) {
    		for (int i=1;i<=(n+1)/2;i++) {
    			if (fstDoubles.get(i).size()==1) {
    				answered++;
    				int j = fstDoubles.get(i).get(0);
    				fstDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					int x = Math.min(i,secGoSize+1-i);
    					if (secDoubles.get(x).contains(j)) secDoubles.get(x).remove(secDoubles.get(x).indexOf(j));
    					if (i==(n+1)/2&&n%2==1) {
    						if (thdDoubles.get(0).contains(j)) thdDoubles.get(0).remove(thdDoubles.get(0).indexOf(j));
    					}
    				}
    				else {
    					answer[n-i]=j;
    					int x = Math.min(n-i+1-secGoSize,thdGoSize+1-(n-i+1-secGoSize));
    					if (thdDoubles.get(x).contains(j)) thdDoubles.get(x).remove(thdDoubles.get(x).indexOf(j));
    				}
    			}

    			if (secDoubles.get(i).size()==1) {
    				answered++;
    				int j = secDoubles.get(i).get(0);
    				secDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					if (secDoubles.get(i).contains(j)) secDoubles.get(i).remove(secDoubles.get(i).indexOf(j));
    				}
    				else {
    					answer[secGoSize-i]=j;
    					if (fstDoubles.get(secGoSize-i+1).contains(j)) fstDoubles.get(secGoSize-i+1).remove(fstDoubles.get(secGoSize-i+1).indexOf(j));
    					if (secGoSize-i+1==(n+1)/2&&n%2==1)
    						if (thdDoubles.get(0).contains(j)) thdDoubles.get(0).remove(thdDoubles.get(0).indexOf(j));
    				}
    			}

    			if (thdDoubles.get(i).size()==1) {
    				answered++;
    				int j = thdDoubles.get(i).get(0);
    				thdDoubles.get(i).remove(0);
    				if (answer[secGoSize+i-1]==-1) {
    					answer[secGoSize+i-1]=j;
    					if (fstDoubles.get(n-(secGoSize+i)+1).contains(j)) fstDoubles.get(n-(secGoSize+i)+1).remove(fstDoubles.get(n-(secGoSize+i)+1).indexOf(j));
    					if (i==1&&n%2==1) {
    						//System.out.println(i+" "+j);
    						if (secDoubles.get(1).contains(j)) secDoubles.get(1).remove(secDoubles.get(0).indexOf(j));
    					}
    				}
    				else {
    					answer[n-1-i]=j;
    					if (fstDoubles.get(i+1).contains(j)) fstDoubles.get(n-(n-i)+1).remove(fstDoubles.get(n-(n-i)+1).indexOf(j));
    				}
    			}
    		}
    	}
    	out.print("! ");
    	for (int i=0;i<n;i++) out.print((char)(answer[i]+'a'));
    	out.println();
    	out.flush();
    	} else {
    		String s = "";
    		for (int i=0;i<n;i++) {
    			out.println("? 1 "+i);
    			out.flush();
    			s+=reader.readLine();
    		}
    		out.println("! "+s);
    		out.flush();
    	}
        reader.close();
        out.close();
    }
}


//package main;
import java.io.*;
import java.util.*;
import java.lang.*;
import java.math.*;
public class Submission {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter (System.out);
        //BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt")));
    	StringTokenizer st = new StringTokenizer(reader.readLine()," ");
    	int answered=0;
    	int n = Integer.parseInt(st.nextToken());
    	if (n>3) {
    	int[] vals = new int[26];
    	int[] answer= new int[n];
    	for (int i=0;i<n;i++) answer[i]=-1;
    	int[][] instancesOfSize = new int[26][n+1];
    	out.println("? 1 "+n);
    	out.flush();
    	for (int i=0;i<n*(n+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()==1) vals[s.charAt(0)-'a']++;
    		if (s.length()<=(n+1)/2)
    			for (int j=0;j<s.length();j++)
    				instancesOfSize[s.charAt(j)-'a'][s.length()]++;
    	}
    	int secGoSize = (n+1)/2;
    	int[][] secGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? 1 "+secGoSize);
    	out.flush();
    	for (int i=0;i<secGoSize*(secGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(secGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				secGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int thdGoSize = (n-1)/2;
    	int[][] thdGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? "+(n/2+1)+" "+(thdGoSize+(n/2+1)-1));
    	out.flush();
    	for (int i=0;i<thdGoSize*(thdGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(thdGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				thdGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int last=0;
    	for (int i=0;i<26;i++)
    		if (vals[i]==1) last=i;
    	for (int i=0;i<n;i++)
    		instancesOfSize[last][i+1]--;

    	//first double-ifier
    	List<List<Integer>> fstDoubles = new ArrayList<>();
    	for (int i=0;i<n+1;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(n+1)/2;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(n+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			//if (j==1) System.out.println(instancesOfSize[j][i]+" "+i);
    			if (instancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    			if (instancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (instancesOfSize[i][(n+1)/2]!=0) {
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    		if (instancesOfSize[i][(n+1)/2]!=0) {//again!
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    	}
    	answer[n-1]=last;
    	int first = fstDoubles.get(1).get(0);
    	fstDoubles.get(1).remove(0);
    	answer[0]=first;
    	answered+=2;
    	//for (int i=0;i<fstDoubles.get((n+1)/2).size();i++) System.out.println(fstDoubles.get((n+1)/2).get(i));

    	//second doubles run
    	for (int i=0;i<=secGoSize;i++)
    		secGoInstancesOfSize[first][i+1]--;
    	List<List<Integer>> secDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) secDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(secGoSize+1)/2;i++) secDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(secGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (secGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (secGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {//again!
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    	}
    	int interim = secDoubles.get(1).get(0);
    	secDoubles.get(1).remove(0);
    	answer[secGoSize-1]=interim;
    	answered++;
    	//System.out.println(fstDoubles.get((n+1)/2).get(0));
    	fstDoubles.get((n+1)/2).remove(fstDoubles.get((n+1)/2).indexOf(interim));
    	if (n%2==0) {
        	interim=fstDoubles.get((n+1)/2).get(0);
        	fstDoubles.get((n+1)/2).remove(0);
        	answer[secGoSize]=interim;
        	answered++;
    	}

    	//THIRD doubles run
    	for (int i=0;i<=thdGoSize;i++)
    		thdGoInstancesOfSize[interim][i+1]--;
    	List<List<Integer>> thdDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(thdGoSize+1)/2;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(thdGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (thdGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (thdGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {//again!
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    	}
    	//WE DID ITTT
    	/*System.out.println("hh");
    	for (int i=0;i<fstDoubles.size();i++) {
    		if (fstDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<fstDoubles.get(i).size();j++) System.out.print(fstDoubles.get(i).get(j)+" ");
    		if (fstDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<secDoubles.size();i++) {
    		if (secDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<secDoubles.get(i).size();j++) System.out.print(secDoubles.get(i).get(j)+" ");
    		if (secDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<thdDoubles.size();i++) {
    		if (thdDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<thdDoubles.get(i).size();j++) System.out.print(thdDoubles.get(i).get(j)+" ");
    		if (thdDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();*/
    	//System.out.println(answered);
    	while (answered<n) {
    		for (int i=1;i<=(n+1)/2;i++) {
    			if (fstDoubles.get(i).size()==1) {
    				answered++;
    				int j = fstDoubles.get(i).get(0);
    				fstDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					int x = Math.min(i,secGoSize+1-i);
    					if (secDoubles.get(x).contains(j)) secDoubles.get(x).remove(secDoubles.get(x).indexOf(j));
    					if (i==(n+1)/2&&n%2==1) {
    						if (thdDoubles.get(0).contains(j)) thdDoubles.get(0).remove(thdDoubles.get(0).indexOf(j));
    					}
    				}
    				else {
    					answer[n-i]=j;
    					int x = Math.min(n-i+1-secGoSize,thdGoSize+1-(n-i+1-secGoSize));
    					if (thdDoubles.get(x).contains(j)) thdDoubles.get(x).remove(thdDoubles.get(x).indexOf(j));
    				}
    			}

    			if (secDoubles.get(i).size()==1) {
    				answered++;
    				int j = secDoubles.get(i).get(0);
    				secDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					if (secDoubles.get(i).contains(j)) secDoubles.get(i).remove(secDoubles.get(i).indexOf(j));
    				}
    				else {
    					answer[secGoSize-i]=j;
    					if (fstDoubles.get(secGoSize-i+1).contains(j)) fstDoubles.get(secGoSize-i+1).remove(fstDoubles.get(secGoSize-i+1).indexOf(j));
    					if (secGoSize-i+1==(n+1)/2&&n%2==1)
    						if (thdDoubles.get(0).contains(j)) thdDoubles.get(0).remove(thdDoubles.get(0).indexOf(j));
    				}
    			}

    			if (thdDoubles.get(i).size()==1) {
    				answered++;
    				int j = thdDoubles.get(i).get(0);
    				thdDoubles.get(i).remove(0);
    				if (answer[secGoSize+i-1]==-1) {
    					answer[secGoSize+i-1]=j;
    					if (fstDoubles.get(n-(secGoSize+i)+1).contains(j)) fstDoubles.get(n-(secGoSize+i)+1).remove(fstDoubles.get(n-(secGoSize+i)+1).indexOf(j));
    					if (i==1&&n%2==1) {
    						//System.out.println(i+" "+j);
    						if (secDoubles.get(1).contains(j)) secDoubles.get(1).remove(secDoubles.get(0).indexOf(j));
    					}
    				}
    				else {
    					answer[n-1-i]=j;
    					if (fstDoubles.get(i+1).contains(j)) fstDoubles.get(n-(n-i)+1).remove(fstDoubles.get(n-(n-i)+1).indexOf(j));
    				}
    			}
    		}
    	}
    	out.print("! ");
    	for (int i=0;i<n;i++) out.print((char)(answer[i]+'a'));
    	out.println();
    	out.flush();
    	} else {
    		String s = "";
    		for (int i=0;i<n;i++) {
    			out.println("? "+(i+1)+" "+(i+1));
    			out.flush();
    			s+=reader.readLine();
    		}
    		out.println("! "+s);
    		out.flush();
    	}
        reader.close();
        out.close();
    }
}


//package main;
import java.io.*;
import java.util.*;
import java.lang.*;
import java.math.*;
public class Submission {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter (System.out);
        //BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt")));
    	StringTokenizer st = new StringTokenizer(reader.readLine()," ");
    	int answered=0;
    	int n = Integer.parseInt(st.nextToken());
    	if (n>3) {
    	int[] vals = new int[26];
    	int[] answer= new int[n];
    	for (int i=0;i<n;i++) answer[i]=-1;
    	int[][] instancesOfSize = new int[26][n+1];
    	out.println("? 1 "+n);
    	out.flush();
    	for (int i=0;i<n*(n+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()==1) vals[s.charAt(0)-'a']++;
    		if (s.length()<=(n+1)/2)
    			for (int j=0;j<s.length();j++)
    				instancesOfSize[s.charAt(j)-'a'][s.length()]++;
    	}
    	int secGoSize = (n+1)/2;
    	int[][] secGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? 1 "+secGoSize);
    	out.flush();
    	for (int i=0;i<secGoSize*(secGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(secGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				secGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int thdGoSize = (n-1)/2;
    	int[][] thdGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? "+(n/2+1)+" "+(thdGoSize+(n/2+1)-1));
    	out.flush();
    	for (int i=0;i<thdGoSize*(thdGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(thdGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				thdGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int last=0;
    	for (int i=0;i<26;i++)
    		if (vals[i]==1) last=i;
    	for (int i=0;i<n;i++)
    		instancesOfSize[last][i+1]--;

    	//first double-ifier
    	List<List<Integer>> fstDoubles = new ArrayList<>();
    	for (int i=0;i<n+1;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(n+1)/2;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(n+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			//if (j==1) System.out.println(instancesOfSize[j][i]+" "+i);
    			if (instancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    			if (instancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (instancesOfSize[i][(n+1)/2]!=0) {
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    		if (instancesOfSize[i][(n+1)/2]!=0) {//again!
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    	}
    	answer[n-1]=last;
    	int first = fstDoubles.get(1).get(0);
    	fstDoubles.get(1).remove(0);
    	answer[0]=first;
    	answered+=2;
    	//for (int i=0;i<fstDoubles.get((n+1)/2).size();i++) System.out.println(fstDoubles.get((n+1)/2).get(i));

    	//second doubles run
    	for (int i=0;i<=secGoSize;i++)
    		secGoInstancesOfSize[first][i+1]--;
    	List<List<Integer>> secDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) secDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(secGoSize+1)/2;i++) secDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(secGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (secGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (secGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {//again!
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    	}
    	int interim = secDoubles.get(1).get(0);
    	secDoubles.get(1).remove(0);
    	answer[secGoSize-1]=interim;
    	answered++;
    	//System.out.println(fstDoubles.get((n+1)/2).get(0));
    	fstDoubles.get((n+1)/2).remove(fstDoubles.get((n+1)/2).indexOf(interim));
    	if (n%2==0) {
        	interim=fstDoubles.get((n+1)/2).get(0);
        	fstDoubles.get((n+1)/2).remove(0);
        	answer[secGoSize]=interim;
        	answered++;
    	}

    	//THIRD doubles run
    	for (int i=0;i<=thdGoSize;i++)
    		thdGoInstancesOfSize[interim][i+1]--;
    	List<List<Integer>> thdDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(thdGoSize+1)/2;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(thdGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (thdGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (thdGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {//again!
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    	}
    	//WE DID ITTT
    	/*System.out.println("hh");
    	for (int i=0;i<fstDoubles.size();i++) {
    		if (fstDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<fstDoubles.get(i).size();j++) System.out.print(fstDoubles.get(i).get(j)+" ");
    		if (fstDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<secDoubles.size();i++) {
    		if (secDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<secDoubles.get(i).size();j++) System.out.print(secDoubles.get(i).get(j)+" ");
    		if (secDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<thdDoubles.size();i++) {
    		if (thdDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<thdDoubles.get(i).size();j++) System.out.print(thdDoubles.get(i).get(j)+" ");
    		if (thdDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();*/
    	//System.out.println(answered);
    	while (answered<n) {
    		for (int i=1;i<=(n+1)/2;i++) {
    			if (fstDoubles.get(i).size()==1) {
    				answered++;
    				int j = fstDoubles.get(i).get(0);
    				fstDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					int x = Math.min(i,secGoSize+1-i);
    					if (secDoubles.get(x).contains(j)) secDoubles.get(x).remove(secDoubles.get(x).indexOf(j));
    					if (i==(n+1)/2&&n%2==1) {
    						if (thdDoubles.get(0).contains(j)) thdDoubles.get(0).remove(thdDoubles.get(0).indexOf(j));
    					}
    				}
    				else {
    					answer[n-i]=j;
    					int x = Math.min(n-i+1-secGoSize,thdGoSize+1-(n-i+1-secGoSize));
    					if (thdDoubles.get(x).contains(j)) thdDoubles.get(x).remove(thdDoubles.get(x).indexOf(j));
    				}
    			}

    			if (secDoubles.get(i).size()==1) {
    				answered++;
    				int j = secDoubles.get(i).get(0);
    				secDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					if (secDoubles.get(i).contains(j)) secDoubles.get(i).remove(secDoubles.get(i).indexOf(j));
    				}
    				else {
    					answer[secGoSize-i]=j;
    					if (fstDoubles.get(secGoSize-i+1).contains(j)) fstDoubles.get(secGoSize-i+1).remove(fstDoubles.get(secGoSize-i+1).indexOf(j));
    					if (secGoSize-i+1==(n+1)/2&&n%2==1)
    						if (thdDoubles.get(0).contains(j)) thdDoubles.get(0).remove(thdDoubles.get(0).indexOf(j));
    				}
    			}

    			if (thdDoubles.get(i).size()==1) {
    				answered++;
    				int j = thdDoubles.get(i).get(0);
    				thdDoubles.get(i).remove(0);
    				if (answer[secGoSize+i-1]==-1) {
    					answer[secGoSize+i-1]=j;
    					if (fstDoubles.get(n-(secGoSize+i)+1).contains(j)) fstDoubles.get(n-(secGoSize+i)+1).remove(fstDoubles.get(n-(secGoSize+i)+1).indexOf(j));
    					if (i==1&&n%2==1) {
    						//System.out.println(i+" "+j);
    						if (secDoubles.get(1).contains(j)) secDoubles.get(1).remove(secDoubles.get(0).indexOf(j));
    					}
    				}
    				else {
    					answer[n-1-i]=j;
    					if (fstDoubles.get(i+1).contains(j)) fstDoubles.get(n-(n-i)+1).remove(fstDoubles.get(n-(n-i)+1).indexOf(j));
    				}
    			}
    		}
    	}
    	out.print("! ");
    	for (int i=0;i<n;i++) out.print((char)(answer[i]+'a'));
    	out.println();
    	out.flush();
    	} else {
    		String s = "";
    		for (int i=0;i<n;i++) {
    			out.println("? 1 "+(i+1));
    			out.flush();
    			s+=reader.readLine();
    		}
    		out.println("! "+s);
    		out.flush();
    	}
        reader.close();
        out.close();
    }
}


//package main;
import java.io.*;
import java.util.*;
import java.lang.*;
import java.math.*;
public class Submission {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter (System.out);
        //BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt")));
    	StringTokenizer st = new StringTokenizer(reader.readLine()," ");
    	int answered=0;
    	int n = Integer.parseInt(st.nextToken());
    	if (n>3) {
    	int[] vals = new int[26];
    	int[] answer= new int[n];
    	for (int i=0;i<n;i++) answer[i]=-1;
    	int[][] instancesOfSize = new int[26][n+1];
    	out.println("? 1 "+n);
    	out.flush();
    	for (int i=0;i<n*(n+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()==1) vals[s.charAt(0)-'a']++;
    		if (s.length()<=(n+1)/2)
    			for (int j=0;j<s.length();j++)
    				instancesOfSize[s.charAt(j)-'a'][s.length()]++;
    	}
    	int secGoSize = (n+1)/2;
    	int[][] secGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? 1 "+secGoSize);
    	out.flush();
    	for (int i=0;i<secGoSize*(secGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(secGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				secGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int thdGoSize = (n-1)/2;
    	int[][] thdGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? "+(n/2+1)+" "+(thdGoSize+(n/2+1)-1));
    	out.flush();
    	for (int i=0;i<thdGoSize*(thdGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(thdGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				thdGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int last=0;
    	for (int i=0;i<26;i++)
    		if (vals[i]==1) last=i;
    	for (int i=0;i<n;i++)
    		instancesOfSize[last][i+1]--;

    	//first double-ifier
    	List<List<Integer>> fstDoubles = new ArrayList<>();
    	for (int i=0;i<n+1;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(n+1)/2;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(n+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			//if (j==1) System.out.println(instancesOfSize[j][i]+" "+i);
    			if (instancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    			if (instancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (instancesOfSize[i][(n+1)/2]!=0) {
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    		if (instancesOfSize[i][(n+1)/2]!=0) {//again!
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    	}
    	answer[n-1]=last;
    	int first = fstDoubles.get(1).get(0);
    	fstDoubles.get(1).remove(0);
    	answer[0]=first;
    	answered+=2;
    	//for (int i=0;i<fstDoubles.get((n+1)/2).size();i++) System.out.println(fstDoubles.get((n+1)/2).get(i));

    	//second doubles run
    	for (int i=0;i<=secGoSize;i++)
    		secGoInstancesOfSize[first][i+1]--;
    	List<List<Integer>> secDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) secDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(secGoSize+1)/2;i++) secDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(secGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (secGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (secGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {//again!
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    	}
    	int interim = secDoubles.get(1).get(0);
    	secDoubles.get(1).remove(0);
    	answer[secGoSize-1]=interim;
    	answered++;
    	//System.out.println(fstDoubles.get((n+1)/2).get(0));
    	fstDoubles.get((n+1)/2).remove(fstDoubles.get((n+1)/2).indexOf(interim));
    	if (n%2==0) {
        	interim=fstDoubles.get((n+1)/2).get(0);
        	fstDoubles.get((n+1)/2).remove(0);
        	answer[secGoSize]=interim;
        	answered++;
    	}

    	//THIRD doubles run
    	for (int i=0;i<=thdGoSize;i++)
    		thdGoInstancesOfSize[interim][i+1]--;
    	List<List<Integer>> thdDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(thdGoSize+1)/2;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(thdGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (thdGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (thdGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {//again!
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    	}
    	//WE DID ITTT
    	/*System.out.println("hh");
    	for (int i=0;i<fstDoubles.size();i++) {
    		if (fstDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<fstDoubles.get(i).size();j++) System.out.print(fstDoubles.get(i).get(j)+" ");
    		if (fstDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<secDoubles.size();i++) {
    		if (secDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<secDoubles.get(i).size();j++) System.out.print(secDoubles.get(i).get(j)+" ");
    		if (secDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<thdDoubles.size();i++) {
    		if (thdDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<thdDoubles.get(i).size();j++) System.out.print(thdDoubles.get(i).get(j)+" ");
    		if (thdDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();*/
    	//System.out.println(answered);
    	//System.out.println(thdDoubles.get(2).size());
    	int count=0;
    	while (answered<n) {

    		for (int i=1;i<=(n+1)/2;i++) {
    			/*count++;
        		if (count<10) {
        		for (int j=0;j<n;j++) System.out.print((char)(answer[j]+'a')+" ");
        		//System.out.println();}*/
    			if (fstDoubles.get(i).size()==1) {
    				answered++;
    				int j = fstDoubles.get(i).get(0);
    				fstDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					int x = Math.min(i,secGoSize+1-i);
    					if (secDoubles.get(x).contains(j)) secDoubles.get(x).remove(secDoubles.get(x).indexOf(j));
    					if (i==(n+1)/2&&n%2==1) {
    						if (thdDoubles.get(1).contains(j)) thdDoubles.get(1).remove(thdDoubles.get(0).indexOf(j));
    					}
    				}
    				else {
    					answer[n-i]=j;
    					int x = Math.min(n-i+1-secGoSize+n%2,thdGoSize+1-(n-i+1-secGoSize)+n%2);
    					if (thdDoubles.get(x).contains(j)) thdDoubles.get(x).remove(thdDoubles.get(x).indexOf(j));
    				}
    			}

    			if (secDoubles.get(i).size()==1) {
    				answered++;
    				int j = secDoubles.get(i).get(0);
    				secDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					if (secDoubles.get(i).contains(j)) secDoubles.get(i).remove(secDoubles.get(i).indexOf(j));
    				}
    				else {
    					answer[secGoSize-i]=j;
    					if (fstDoubles.get(secGoSize-i+1).contains(j)) fstDoubles.get(secGoSize-i+1).remove(fstDoubles.get(secGoSize-i+1).indexOf(j));
    					if (secGoSize-i+1==(n+1)/2&&n%2==1)
    						if (thdDoubles.get(1).contains(j)) thdDoubles.get(1).remove(thdDoubles.get(0).indexOf(j));
    				}
    			}

    			if (thdDoubles.get(i).size()==1) {
    				answered++;
    				int j = thdDoubles.get(i).get(0);
    				thdDoubles.get(i).remove(0);
    				if (answer[secGoSize+i-1-(n)%2]==-1) {
        				//System.out.println("j");
    					answer[secGoSize+i-1-(n)%2]=j;
    					if (fstDoubles.get(n-(secGoSize+i)+1).contains(j)) fstDoubles.get(n-(secGoSize+i)+1).remove(fstDoubles.get(n-(secGoSize+i)+1).indexOf(j));
    					if (i==1&&n%2==1) {
    						//System.out.println(i+" "+j);
    						if (secDoubles.get(1).contains(j)) secDoubles.get(1).remove(secDoubles.get(0).indexOf(j));
    					}
    				}
    				else {
    					answer[n-1-i]=j;
    					if (fstDoubles.get(i+1).contains(j)) fstDoubles.get(n-(n-i)+1).remove(fstDoubles.get(n-(n-i)+1).indexOf(j));
    				}
    			}
    		}
    	}
    	out.print("! ");
    	for (int i=0;i<n;i++) out.print((char)(answer[i]+'a'));
    	out.println();
    	out.flush();
    	} else {
    		String s = "";
    		for (int i=0;i<n;i++) {
    			out.println("? "+(i+1)+" "+(i+1));
    			out.flush();
    			s+=reader.readLine();
    		}
    		out.println("! "+s);
    		out.flush();
    	}
        reader.close();
        out.close();
    }
}


//package main;
import java.io.*;
import java.util.*;
import java.lang.*;
import java.math.*;
public class Submission {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter (System.out);
        //BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt")));
    	StringTokenizer st = new StringTokenizer(reader.readLine()," ");
    	int answered=0;
    	int n = Integer.parseInt(st.nextToken());
    	if (n>3) {
    	int[] vals = new int[26];
    	int[] answer= new int[n];
    	for (int i=0;i<n;i++) answer[i]=-1;
    	int[][] instancesOfSize = new int[26][n+1];
    	out.println("? 1 "+n);
    	out.flush();
    	for (int i=0;i<n*(n+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()==1) vals[s.charAt(0)-'a']++;
    		if (s.length()<=(n+1)/2)
    			for (int j=0;j<s.length();j++)
    				instancesOfSize[s.charAt(j)-'a'][s.length()]++;
    	}
    	int secGoSize = (n+3)/2;
    	int[][] secGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? 1 "+secGoSize);
    	out.flush();
    	for (int i=0;i<secGoSize*(secGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(secGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				secGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int thdGoSize = (n)/2;
    	int[][] thdGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? "+secGoSize+" "+n);
    	out.flush();
    	for (int i=0;i<thdGoSize*(thdGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(thdGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				thdGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int mid=0;
    	for (int i=0;i<26;i++)
    		if (vals[i]==-1) mid=i;
    	for (int i=0;i<n;i++)
    		secGoInstancesOfSize[mid][i+1]--;
    	for (int i=0;i<n;i++)
    		thdGoInstancesOfSize[mid][i+1]--;

    	//second doubles run
    	List<List<Integer>> secDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) secDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(secGoSize+1)/2;i++) secDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(secGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (secGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (secGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {//again!
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    	}
    	int first = secDoubles.get(1).get(0);
    	secDoubles.get(1).remove(0);
    	secDoubles.get(1).add(mid);
    	answer[0]=first;
    	answered++;

    	//THIRD doubles run
    	List<List<Integer>> thdDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(thdGoSize+1)/2;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(thdGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (thdGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (thdGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {//again!
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    	}
    	thdDoubles.get(1).add(mid);

    	//first double-ifier
    	for (int i=0;i<=secGoSize;i++)
    		instancesOfSize[first][i+1]--;
    	List<List<Integer>> fstDoubles = new ArrayList<>();
    	for (int i=0;i<n+1;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(n+1)/2;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(n+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			//if (j==1) System.out.println(instancesOfSize[j][i]+" "+i);
    			if (instancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    			if (instancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (instancesOfSize[i][(n+1)/2]!=0) {
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    		if (instancesOfSize[i][(n+1)/2]!=0) {//again!
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    	}
    	//for (int i=0;i<fstDoubles.get((n+1)/2).size();i++) System.out.println(fstDoubles.get((n+1)/2).get(i));

    	//WE DID ITTT
    	/*System.out.println("hh");
    	for (int i=0;i<fstDoubles.size();i++) {
    		if (fstDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<fstDoubles.get(i).size();j++) System.out.print(fstDoubles.get(i).get(j)+" ");
    		if (fstDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<secDoubles.size();i++) {
    		if (secDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<secDoubles.get(i).size();j++) System.out.print(secDoubles.get(i).get(j)+" ");
    		if (secDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<thdDoubles.size();i++) {
    		if (thdDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<thdDoubles.get(i).size();j++) System.out.print(thdDoubles.get(i).get(j)+" ");
    		if (thdDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();*/
    	//System.out.println(answered);
    	//System.out.println(thdDoubles.get(2).size());
    	int count=0;
    	while (answered<n) {

    		for (int i=1;i<=(n+1)/2;i++) {
    			/*count++;
        		if (count<1000) {
        		for (int j=0;j<n;j++) System.out.print((char)(answer[j]+'a')+" ");
        		System.out.println();}*/
    			if (fstDoubles.get(i).size()==1) {
    				//System.out.println(fstDoubles.get(i).get(0));
    				answered++;
    				int j = fstDoubles.get(i).get(0);
    				fstDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					int x = Math.min(i,secGoSize+1-i);
    					if (secDoubles.get(x).contains(j)) secDoubles.get(x).remove(secDoubles.get(x).indexOf(j));
    				}
    				else {
    					answer[n-i]=j;
    					int x = Math.min(n-i+1-secGoSize+1,thdGoSize+1-(n-i+1-secGoSize+1));
    					if (thdDoubles.get(x).contains(j)) thdDoubles.get(x).remove(thdDoubles.get(x).indexOf(j));
    				}
    			}

    			if (secDoubles.get(i).size()==1) {
    				answered++;
    				//System.out.println("zzz");
    				int j = secDoubles.get(i).get(0);
    				secDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					if (fstDoubles.get(i).contains(j)) fstDoubles.get(i).remove(fstDoubles.get(i).indexOf(j));
    				}
    				else {
    					answer[secGoSize-i]=j;
    					int x = Math.min(secGoSize-i+1, n+1-(secGoSize-i+1));
    					if (fstDoubles.get(x).contains(j)) fstDoubles.get(x).remove(fstDoubles.get(x).indexOf(j));
    					if (i==1)
    						if (thdDoubles.get(1).contains(j)) thdDoubles.get(1).remove(thdDoubles.get(1).indexOf(j));
    				}
    			}

    			if (thdDoubles.get(i).size()==1) {
    				answered++;
    				int j = thdDoubles.get(i).get(0);
    				thdDoubles.get(i).remove(0);
    				if (answer[secGoSize+i-1-1]==-1) {
        				//System.out.println("j");
    					answer[secGoSize+i-1-1]=j;
    					if (fstDoubles.get(n-(secGoSize+i-1)+1).contains(j)) fstDoubles.get(n-(secGoSize+i-1)+1).remove(fstDoubles.get(n-(secGoSize+i-1)+1).indexOf(j));
    					if (i==1) {
    						//System.out.println(i+" "+j);
    						if (secDoubles.get(1).contains(j)) secDoubles.get(1).remove(secDoubles.get(1).indexOf(j));
    					}
    				}
    				else {
    					answer[n-1-i+1]=j;
    					if (fstDoubles.get(i).contains(j)) fstDoubles.get(i).remove(fstDoubles.get(i).indexOf(j));
    				}
    			}
    		}
    	}
    	out.print("! ");
    	for (int i=0;i<n;i++) out.print((char)(answer[i]+'a'));
    	out.println();
    	out.flush();
    	} else {
    		String s = "";
    		for (int i=0;i<n;i++) {
    			out.println("? "+(i+1)+" "+(i+1));
    			out.flush();
    			s+=reader.readLine();
    		}
    		out.println("! "+s);
    		out.flush();
    	}
        reader.close();
        out.close();
    }
}


//package main;
import java.io.*;
import java.util.*;
import java.lang.*;
import java.math.*;
public class Submission {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter (System.out);
        //BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt")));
    	StringTokenizer st = new StringTokenizer(reader.readLine()," ");
    	int answered=0;
    	int n = Integer.parseInt(st.nextToken());
    	if (n>3) {
    	int[] vals = new int[26];
    	int[] answer= new int[n];
    	for (int i=0;i<n;i++) answer[i]=-1;
    	int[][] instancesOfSize = new int[26][n+1];
    	out.println("? 1 "+n);
    	out.flush();
    	for (int i=0;i<n*(n+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()==1) vals[s.charAt(0)-'a']++;
    		if (s.length()<=(n+1)/2)
    			for (int j=0;j<s.length();j++)
    				instancesOfSize[s.charAt(j)-'a'][s.length()]++;
    	}
    	int secGoSize = (n+1)/2;
    	int[][] secGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? 1 "+secGoSize);
    	out.flush();
    	for (int i=0;i<secGoSize*(secGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(secGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				secGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int thdGoSize = (n-1)/2;
    	int[][] thdGoInstancesOfSize = new int[26][n+1]; // iterations at size of letter for left
    	out.println("? "+(n/2+1)+" "+(thdGoSize+(n/2+1)-1));
    	out.flush();
    	for (int i=0;i<thdGoSize*(thdGoSize+1)/2;i++) {
    		String s = reader.readLine();
    		if (s.length()<=(thdGoSize+1)/2)
    			for (int j=0;j<s.length();j++)
    				thdGoInstancesOfSize[s.charAt(j)-'a'][s.length()]++;
    		if (s.length()==1) vals[s.charAt(0)-'a']--;
    	}
    	int last=0;
    	for (int i=0;i<26;i++)
    		if (vals[i]==1) last=i;
    	for (int i=0;i<n;i++)
    		instancesOfSize[last][i+1]--;

    	//first double-ifier
    	List<List<Integer>> fstDoubles = new ArrayList<>();
    	for (int i=0;i<n+1;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(n+1)/2;i++) fstDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(n+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			//if (j==1) System.out.println(instancesOfSize[j][i]+" "+i);
    			if (instancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    			if (instancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				fstDoubles.get(i-1).add(j);
    				for (int k=0;k<=n;k++) instancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (instancesOfSize[i][(n+1)/2]!=0) {
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    		if (instancesOfSize[i][(n+1)/2]!=0) {//again!
    			instancesOfSize[i][(n+1)/2]-=(n+1)/2;
    			fstDoubles.get((n+1)/2).add(i);
    		}
    	}
    	answer[n-1]=last;
    	int first = fstDoubles.get(1).get(0);
    	fstDoubles.get(1).remove(0);
    	answer[0]=first;
    	answered+=2;
    	//for (int i=0;i<fstDoubles.get((n+1)/2).size();i++) System.out.println(fstDoubles.get((n+1)/2).get(i));

    	//second doubles run
    	for (int i=0;i<=secGoSize;i++)
    		secGoInstancesOfSize[first][i+1]--;
    	List<List<Integer>> secDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) secDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(secGoSize+1)/2;i++) secDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(secGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (secGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (secGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				secDoubles.get(i-1).add(j);
    				for (int k=0;k<=secGoSize;k++) secGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    		if (secGoInstancesOfSize[i][(secGoSize+1)/2]!=0) {//again!
    			secGoInstancesOfSize[i][(secGoSize+1)/2]-=(secGoSize+1)/2;
    			secDoubles.get((secGoSize+1)/2).add(i);
    		}
    	}
    	int interim = secDoubles.get(1).get(0);
    	secDoubles.get(1).remove(0);
    	answer[secGoSize-1]=interim;
    	answered++;
    	//System.out.println(fstDoubles.get((n+1)/2).get(0));
    	fstDoubles.get((n+1)/2).remove(fstDoubles.get((n+1)/2).indexOf(interim));
    	if (n%2==0) {
        	interim=fstDoubles.get((n+1)/2).get(0);
        	fstDoubles.get((n+1)/2).remove(0);
        	answer[secGoSize]=interim;
        	answered++;
    	}

    	//THIRD doubles run
    	for (int i=0;i<=thdGoSize;i++)
    		thdGoInstancesOfSize[interim][i+1]--;
    	List<List<Integer>> thdDoubles = new ArrayList<>();
    	for (int i=0;i<=n;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=0;i<=(thdGoSize+1)/2;i++) thdDoubles.add(new ArrayList<>());
    	for (int i=2;i<=(thdGoSize+1)/2;i++) {
    		for (int j=0;j<26;j++) {
    			if (thdGoInstancesOfSize[j][i]%i!=0) {
    				//out.println((char)(j+'a')+" "+i+" "+instancesOfSize[j][i]);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    			if (thdGoInstancesOfSize[j][i]%i!=0) { //again! for doubles
    				//out.println(j+"XX"+i);
    				thdDoubles.get(i-1).add(j);
    				for (int k=0;k<=thdGoSize;k++) thdGoInstancesOfSize[j][k]-=(i-1);
    			}
    		}
    	}
    	for (int i=0;i<26;i++) {
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    		if (thdGoInstancesOfSize[i][(thdGoSize+1)/2]!=0) {//again!
    			thdGoInstancesOfSize[i][(thdGoSize+1)/2]-=(thdGoSize+1)/2;
    			thdDoubles.get((thdGoSize+1)/2).add(i);
    		}
    	}
    	//WE DID ITTT
    	/*System.out.println("hh");
    	for (int i=0;i<fstDoubles.size();i++) {
    		if (fstDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<fstDoubles.get(i).size();j++) System.out.print(fstDoubles.get(i).get(j)+" ");
    		if (fstDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<secDoubles.size();i++) {
    		if (secDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<secDoubles.get(i).size();j++) System.out.print(secDoubles.get(i).get(j)+" ");
    		if (secDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();
    	for (int i=0;i<thdDoubles.size();i++) {
    		if (thdDoubles.get(i).size()>0) System.out.print(i+" ");
    		for (int j=0;j<thdDoubles.get(i).size();j++) System.out.print(thdDoubles.get(i).get(j)+" ");
    		if (thdDoubles.get(i).size()>0) System.out.println();
    	}
    	System.out.println();*/
    	//System.out.println(answered);
    	//System.out.println(thdDoubles.get(2).size());
    	int count=0;
    	while (answered<n) {

    		for (int i=1;i<=(n+1)/2;i++) {
    			/*count++;
        		if (count<100) {
        		for (int j=0;j<n;j++) System.out.print((char)(answer[j]+'a')+" ");
        		System.out.println();}*/
    			if (fstDoubles.get(i).size()==1) {
    				//System.out.println(fstDoubles.get(i).get(0));
    				answered++;
    				int j = fstDoubles.get(i).get(0);
    				fstDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					int x = Math.min(i,secGoSize+1-i);
    					if (secDoubles.get(x).contains(j)) secDoubles.get(x).remove(secDoubles.get(x).indexOf(j));
    					if (i==(n+1)/2&&n%2==1) {
    						if (thdDoubles.get(1).contains(j)) thdDoubles.get(1).remove(thdDoubles.get(0).indexOf(j));
    					}
    				}
    				else {
    					answer[n-i]=j;
    					int x = Math.min(n-i+1-secGoSize+n%2,thdGoSize+1-(n-i+1-secGoSize)+n%2);
    					if (thdDoubles.get(x).contains(j)) thdDoubles.get(x).remove(thdDoubles.get(x).indexOf(j));
    				}
    			}

    			if (secDoubles.get(i).size()==1) {
    				answered++;
    				//System.out.println("zzz");
    				int j = secDoubles.get(i).get(0);
    				secDoubles.get(i).remove(0);
    				if (answer[i-1]==-1) {
    					answer[i-1]=j;
    					if (fstDoubles.get(i).contains(j)) fstDoubles.get(i).remove(fstDoubles.get(i).indexOf(j));
    				}
    				else {
    					answer[secGoSize-i]=j;
    					if (fstDoubles.get(secGoSize-i+1).contains(j)) fstDoubles.get(secGoSize-i+1).remove(fstDoubles.get(secGoSize-i+1).indexOf(j));
    					if (secGoSize-i+1==(n+1)/2&&n%2==1)
    						if (thdDoubles.get(1).contains(j)) thdDoubles.get(1).remove(thdDoubles.get(0).indexOf(j));
    				}
    			}

    			if (thdDoubles.get(i).size()==1) {
    				answered++;
    				int j = thdDoubles.get(i).get(0);
    				thdDoubles.get(i).remove(0);
    				if (answer[secGoSize+i-1-(n)%2]==-1) {
        				//System.out.println("j");
    					answer[secGoSize+i-1-(n)%2]=j;
    					if (fstDoubles.get(n-(secGoSize+i)+1).contains(j)) fstDoubles.get(n-(secGoSize+i)+1).remove(fstDoubles.get(n-(secGoSize+i)+1).indexOf(j));
    					if (i==1&&n%2==1) {
    						//System.out.println(i+" "+j);
    						if (secDoubles.get(1).contains(j)) secDoubles.get(1).remove(secDoubles.get(0).indexOf(j));
    					}
    				}
    				else {
    					answer[n-1-i]=j;
    					if (fstDoubles.get(i+1).contains(j)) fstDoubles.get(n-(n-i)+1).remove(fstDoubles.get(n-(n-i)+1).indexOf(j));
    				}
    			}
    		}
    	}
    	out.print("! ");
    	for (int i=0;i<n;i++) out.print((char)(answer[i]+'a'));
    	out.println();
    	out.flush();
    	} else {
    		String s = "";
    		for (int i=0;i<n;i++) {
    			out.println("? "+(i+1)+" "+(i+1));
    			out.flush();
    			s+=reader.readLine();
    		}
    		out.println("! "+s);
    		out.flush();
    	}
        reader.close();
        out.close();
    }
}


import java.util.*;
import java.io.*;

public class CF1286C1 {
  public static void main(String[] args) throws IOException {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    int n = Integer.parseInt(in.readLine());
    int h = (n+1)/2;
    System.out.printf("? %d %d\n", 1, h);
    System.out.flush();
    int cnt[][] = new int[n+2][26];
    for (int i = 0; i < h * (h + 1) / 2; i++) {
      String sub = in.readLine();
      if (n == 1) {
        System.out.printf("! %s\n", sub);
        return;
      }
      for (int j = 0; j < sub.length(); j++) {
        cnt[sub.length()][sub.charAt(j) - 'a']++;
      }
    }
    System.out.printf("? %d %d\n", 2, h);
    System.out.flush();
    for (int i = 0; i < h * (h - 1) / 2; i++) {
      String sub = in.readLine();
      for (int j = 0; j < sub.length(); j++) {
        cnt[sub.length()][sub.charAt(j) - 'a']--;
      }
    }
    StringBuilder left = new StringBuilder();
    for (int i = 1; i <= n; i++) {
      for (int j = 0; j < 26; j++) {
        if (cnt[i][j] > 0) {
          left.append((char)('a' + j));
          for (int k = i+1; k <= n; k++) {
            cnt[k][j]--;
          }
          break;
        }
      }
    }
    int cnt2[][] = new int[n+2][26];
    System.out.printf("? %d %d\n", 1, n);
    System.out.flush();
    for (int i = 0; i < n * (n + 1) / 2; i++) {
      String sub = in.readLine();
      for (int j = 0; j < sub.length(); j++) {
        cnt2[sub.length()][sub.charAt(j) - 'a']++;
      }
    }
    // for (int i = 1; i <= n; i++) {
    //   for (int j = 0; j < 26; j++) {
    //     System.out.printf("%d ", cnt2[i][j]);
    //   }
    //   System.out.println();
    // }
    StringBuilder right = new StringBuilder();
    for (int i = n; i > h; i--) {
      int mod = n-i+2;
      // System.out.printf("====== mod %d ======\n", mod);
      for (int j = 0; j < mod-1; j++) {
        cnt2[mod][left.charAt(j) - 'a'] -= (j+1);
      }
      for (int j = 0; j < mod-2; j++) {
        cnt2[mod][right.charAt(j) - 'a'] -= (j+1);
      }
      for (int j = 0; j < 26; j++) {
        // System.out.printf("%d ", cnt2[mod][j] % mod);
        if (cnt2[mod][j] % mod == mod-1) {
          right.append((char)(j + 'a'));
        }
      }
      // System.out.println();
    }

    System.out.println(left.append(right.reverse()));
    System.out.flush();
  }
}


import java.util.*;
import java.io.*;

public class CF1286C1 {
  public static void main(String[] args) throws IOException {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    int n = Integer.parseInt(in.readLine());
    if (n == 1) {
      System.out.printf("? %d %d\n", 1, 1);
      String str = in.readLine();
      System.out.printf("! %s\n", str);
      System.out.flush();
      return;
    } else if (n == 2) {
      System.out.printf("? %d %d\n", 1, 1);
      String str1 = in.readLine();
      System.out.printf("? %d %d\n", 2, 2);
      String str2 = in.readLine();
      System.out.printf("! %s\n", str1 + str2);
      System.out.flush();
      return;
    }
    int h = (n+1)/2;
    System.out.printf("? %d %d\n", 1, h);
    System.out.flush();
    int cnt[][] = new int[n+2][26];
    for (int i = 0; i < h * (h + 1) / 2; i++) {
      String sub = in.readLine();
      for (int j = 0; j < sub.length(); j++) {
        cnt[sub.length()][sub.charAt(j) - 'a']++;
      }
    }
    System.out.printf("? %d %d\n", 2, h);
    System.out.flush();
    for (int i = 0; i < h * (h - 1) / 2; i++) {
      String sub = in.readLine();
      for (int j = 0; j < sub.length(); j++) {
        cnt[sub.length()][sub.charAt(j) - 'a']--;
      }
    }
    StringBuilder left = new StringBuilder();
    for (int i = 1; i <= n; i++) {
      for (int j = 0; j < 26; j++) {
        if (cnt[i][j] > 0) {
          left.append((char)('a' + j));
          for (int k = i+1; k <= n; k++) {
            cnt[k][j]--;
          }
          break;
        }
      }
    }
    int cnt2[][] = new int[n+2][26];
    System.out.printf("? %d %d\n", 1, n);
    System.out.flush();
    for (int i = 0; i < n * (n + 1) / 2; i++) {
      String sub = in.readLine();
      for (int j = 0; j < sub.length(); j++) {
        cnt2[sub.length()][sub.charAt(j) - 'a']++;
      }
    }
    // for (int i = 1; i <= n; i++) {
    //   for (int j = 0; j < 26; j++) {
    //     System.out.printf("%d ", cnt2[i][j]);
    //   }
    //   System.out.println();
    // }
    StringBuilder right = new StringBuilder();
    for (int i = n; i > h; i--) {
      int mod = n-i+2;
      // System.out.printf("====== mod %d ======\n", mod);
      for (int j = 0; j < mod-1; j++) {
        cnt2[mod][left.charAt(j) - 'a'] -= (j+1);
      }
      for (int j = 0; j < mod-2; j++) {
        cnt2[mod][right.charAt(j) - 'a'] -= (j+1);
      }
      for (int j = 0; j < 26; j++) {
        // System.out.printf("%d ", cnt2[mod][j] % mod);
        if (cnt2[mod][j] % mod == mod-1) {
          right.append((char)(j + 'a'));
        }
      }
      // System.out.println();
    }

    System.out.printf("! %s\n", left.append(right.reverse()));
    System.out.flush();
  }
}


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;
import java.io.IOException;
import java.util.HashMap;
import java.util.ArrayList;
import java.io.UncheckedIOException;
import java.util.List;
import java.util.Map;
import java.io.Closeable;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) throws Exception {
        Thread thread = new Thread(null, new TaskAdapter(), "", 1 << 27);
        thread.start();
        thread.join();
    }

    static class TaskAdapter implements Runnable {
        @Override
        public void run() {
            InputStream inputStream = System.in;
            OutputStream outputStream = System.out;
            FastInput in = new FastInput(inputStream);
            FastOutput out = new FastOutput(outputStream);
            C2MadhouseHardVersion solver = new C2MadhouseHardVersion();
            solver.solve(1, in, out);
            out.close();
        }
    }

    static class C2MadhouseHardVersion {
        FastInput in;
        FastOutput out;

        public void solve(int testNumber, FastInput in, FastOutput out) {
            this.in = in;
            this.out = out;

            int n = in.readInt();
            if (n == 1) {
                answer(actual(1));
                return;
            }

            int m = (n + 1) / 2;
            String left = actual(m);
            Map<Summary, Integer> total = ask(1, n);

            char[] ans = new char[n];
            Arrays.fill(ans, 'a');
            for (int i = 0; i < m; i++) {
                ans[i] = left.charAt(i);
            }

            Summary all = null;
            for (Summary s : total.keySet()) {
                if (s.sum == n) {
                    all = s;
                    total.remove(all);
                    break;
                }
            }

            Summary last = all;
            for (int i = n - 1; i >= m; i--) {
                Summary trace = new Summary(all);
                int j;
                for (j = 0; i + j - 1 < n - 1; j++) {
                    trace.dec(ans[j] - 'a');
                }
                for (; j >= 1; j--) {
                    remove(total, trace);
                    trace.inc(ans[j - 1] - 'a');
                    trace.dec(ans[i + j - 1] - 'a');
                }
                Summary now = null;
                for (Summary s : total.keySet()) {
                    if (s.sum == i) {
                        now = s;
                        total.remove(now);
                        break;
                    }
                }

                ans[i] = (char) (last.differIndex(now) + 'a');
                last = now;
            }

            answer(new String(ans));
        }

        public void remove(Map<Summary, Integer> total, Summary s) {
            int remain = total.get(s) - 1;
            if (remain == 0) {
                total.remove(s);
            } else {
                total.put(s, remain);
            }
        }

        public String actual(int m) {
            if (m == 1) {
                Map<Summary, Integer> map = ask(1, 1);
                String s = "" + (char) (map.keySet().iterator().next().differIndex(new Summary("")) + 'a');
                return s;
            }
            Map<Summary, Integer> h1 = ask(1, m);
            Map<Summary, Integer> h2 = ask(2, m);
            for (Summary s : h2.keySet()) {
                int remain = h1.get(s) - h2.get(s);
                if (remain == 0) {
                    h1.remove(s);
                } else {
                    h1.put(s, remain);
                }
            }

            List<Summary> list = new ArrayList<>(h1.keySet());
            list.sort((a, b) -> a.sum - b.sum);
            Summary last = new Summary("");
            StringBuilder ans = new StringBuilder(m);
            for (Summary s : list) {
                ans.append((char) (s.differIndex(last) + 'a'));
                last = s;
            }
            return ans.toString();
        }

        void answer(String ans) {
            out.printf("! %s", ans).println().flush();
        }

        Map<Summary, Integer> ask(int l, int r) {
            int n = r - l + 1;
            int m = n * (n + 1) / 2;
            Map<Summary, Integer> map = new HashMap<>(m);
            out.printf("? %d %d", l, r).println().flush();
            for (int i = 0; i < m; i++) {
                Summary s = new Summary(in.readString());
                map.put(s, map.getOrDefault(s, 0) + 1);
            }
            return map;
        }

    }

    static class SequenceUtils {
        public static boolean equal(int[] a, int al, int ar, int[] b, int bl, int br) {
            if ((ar - al) != (br - bl)) {
                return false;
            }
            for (int i = al, j = bl; i <= ar; i++, j++) {
                if (a[i] != b[j]) {
                    return false;
                }
            }
            return true;
        }

    }

    static class FastOutput implements AutoCloseable, Closeable {
        private StringBuilder cache = new StringBuilder(10 << 20);
        private final Writer os;

        public FastOutput(Writer os) {
            this.os = os;
        }

        public FastOutput(OutputStream os) {
            this(new OutputStreamWriter(os));
        }

        public FastOutput printf(String format, Object... args) {
            cache.append(String.format(format, args));
            return this;
        }

        public FastOutput println() {
            cache.append('\n');
            return this;
        }

        public FastOutput flush() {
            try {
                os.append(cache);
                os.flush();
                cache.setLength(0);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
            return this;
        }

        public void close() {
            flush();
            try {
                os.close();
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        public String toString() {
            return cache.toString();
        }

    }

    static class FastInput {
        private final InputStream is;
        private StringBuilder defaultStringBuf = new StringBuilder(1 << 13);
        private byte[] buf = new byte[1 << 13];
        private int bufLen;
        private int bufOffset;
        private int next;

        public FastInput(InputStream is) {
            this.is = is;
        }

        private int read() {
            while (bufLen == bufOffset) {
                bufOffset = 0;
                try {
                    bufLen = is.read(buf);
                } catch (IOException e) {
                    bufLen = -1;
                }
                if (bufLen == -1) {
                    return -1;
                }
            }
            return buf[bufOffset++];
        }

        public void skipBlank() {
            while (next >= 0 && next <= 32) {
                next = read();
            }
        }

        public int readInt() {
            int sign = 1;

            skipBlank();
            if (next == '+' || next == '-') {
                sign = next == '+' ? 1 : -1;
                next = read();
            }

            int val = 0;
            if (sign == 1) {
                while (next >= '0' && next <= '9') {
                    val = val * 10 + next - '0';
                    next = read();
                }
            } else {
                while (next >= '0' && next <= '9') {
                    val = val * 10 - next + '0';
                    next = read();
                }
            }

            return val;
        }

        public String readString(StringBuilder builder) {
            skipBlank();

            while (next > 32) {
                builder.append((char) next);
                next = read();
            }

            return builder.toString();
        }

        public String readString() {
            defaultStringBuf.setLength(0);
            return readString(defaultStringBuf);
        }

    }

    static class Summary {
        int[] cnts = new int['z' - 'a' + 1];
        int sum;

        public void dec(int i) {
            cnts[i]--;
            sum--;
        }

        public void inc(int i) {
            cnts[i]++;
            sum++;
        }

        public Summary(Summary model) {
            this.cnts = model.cnts.clone();
            this.sum = model.sum;
        }

        public Summary(String s) {
            sum = s.length();
            for (char c : s.toCharArray()) {
                cnts[c - 'a']++;
            }
        }

        public int differIndex(Summary s) {
            for (int i = 0; i < cnts.length; i++) {
                if (cnts[i] != s.cnts[i]) {
                    return i;
                }
            }
            return -1;
        }

        public int hashCode() {
            int hash = 1;
            for (int i = 0; i < cnts.length; i++) {
                hash = hash * 31 + cnts[i];
            }
            return hash;
        }

        public boolean equals(Object obj) {
            if (!(obj instanceof Summary)) {
                return false;
            }
            Summary other = (Summary) obj;
            return SequenceUtils.equal(cnts, 0, cnts.length - 1,
                    other.cnts, 0, cnts.length - 1);
        }

    }
}


#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
int n; char ans[MAXN];
char mp[MAXN][MAXN][2];
bool ok() {
	for (int i = 1; i <= n; i++)
		if (ans[i] == 0) return false;
	return true;
}
vector <string> a[MAXN];
void work(int l, int r) {
	int len = r - l + 1, cnt = len * (len + 1) / 2;
	if (len <= 0) return;
	cout << '?' << ' ' << l << ' ' << r << endl;
	for (int i = 1; i <= len; i++)
		a[i].clear();
	for (int i = 1; i <= cnt; i++) {
		string s; cin >> s;
		a[s.size()].push_back(s);
	}
	int now[256]; memset(now, 0, sizeof(now));
	for (auto x : a[len].back()) now[x]++;
	for (int i = l, j = r, k = 2; i <= j; i++, j--, k++) {
		char x = 0, y = 0;
		int tmp[256]; memcpy(tmp, now, sizeof(now));
		for (int c = 'a'; c <= 'z'; c++)
			tmp[c] *= k;
		for (int ti = l, tj = r, t = 1; ti < i; ti++, tj--, t++) {
			tmp[mp[ti][tj][0]] -= k - t;
			tmp[mp[ti][tj][1]] -= k - t;
		}
		for (auto s : a[len - k + 1])
		for (auto x : s) tmp[x]--;
		for (int c = 'a'; c <= 'z'; c++) {
			while (tmp[c]--) {
				if (x == 0) x = c;
				else {
					assert(y == 0);
					y = c;
				}
			}
		}
		mp[i][j][0] = mp[j][i][0] = x;
		mp[i][j][1] = mp[j][i][1] = y;
	}
}
int main() {
	read(n);
	work(1, n - (n + 1) % 2);
	work(1, n / 2), work(n / 2 + 1, n);
	while (!ok()) {
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) {
			if (mp[i][j][0] != 0 && mp[i][j][0] == mp[i][j][1]) {
				ans[i] = ans[j] = mp[i][j][0];
			} else if (ans[i] == 0 && ans[j] != 0 && mp[i][j][0] != 0) {
				if (ans[j] == mp[i][j][0]) ans[i] = mp[i][j][1];
				else ans[i] = mp[i][j][0];
			}
		}
	}
	printf("! %s", ans + 1); cout << endl;
	return 0;
}


#include <bits/stdc++.h>
#define FOR(i, u, v) for (int i = u; i <= v; i++)
#define FORD(i, v, u) for (int i = v; i >= u; i--)
#define pii pair<int, int>
#define mp make_pair
#define F first
#define S second
#define PB push_back
#define N 105

using namespace std;

int n;

void answer(vector<char> s) {
    cout <<"! ";
    for (auto c : s) cout <<c;
    cout <<endl;
    exit(0);
}
void ask(int u, int v) {
    cout <<"? "<<u<<' '<<v<<endl;
}

int main() {
    cin >> n;
    vector<char> res(n, 0);

    if (n <= 3) {
        FOR(i, 1, n) {
            ask(i, i);
            char c; cin >> c;
            res[i-1] = c;
        }
        answer(res);
    }

    vector<int> cnt[n+1];
    FOR(i, 1, n) cnt[i].resize(27);
    int mid = (n+1)/2;
    ask(1, mid);
    FOR(i, 1, mid*(mid+1)/2) {
        string s; cin >> s;
        for (auto c : s) cnt[s.length()][c-'a']++;
    }
    ask(1, mid-1);
    FOR(i, 1, (mid-1)*mid/2) {
        string s; cin >> s;
        for (auto c : s) cnt[s.length()][c-'a']--;
    }
    FORD(i, mid, 1) {
        char tmp = 'a';
        FOR(c, 'a', 'z')
            if (cnt[mid-i+1][c-'a'] > 0) tmp = c;
        res[i-1] = tmp;
        FOR(len, mid-i+2, mid)
            cnt[len][tmp-'a']--;
    }
    FOR(i, 1, n) {
        cnt[i].resize(0);
        cnt[i].resize(27);
    }
    //answer(res);
    ask(1, n);
    FOR(i, 1, n*(n+1)/2) {
        string s; cin >> s;
        for (auto c : s) cnt[s.length()][c-'a']++;
    }
    FOR(c, 'a', 'z') {
        int curCnt = 0;
        FOR(i, mid-1, 1) {
            int val = cnt[i+1][c-'a'] - cnt[i][c-'a'] - curCnt;
            int pos1 = i+1;
            int pos2 = n-i;
            if (val == 2 || (val == 1 && res[pos1-1] != c)) res[pos2-1] = c;
            curCnt += val;
        }
    }
    FOR(i, 0, n-2) cnt[1][res[i]-'a']--;
    FOR(c, 'a', 'z')
        if (cnt[1][c-'a']) res[n-1] = c;
    answer(res);
}


#include <bits/stdc++.h>
#define FOR(i, u, v) for (int i = u; i <= v; i++)
#define FORD(i, v, u) for (int i = v; i >= u; i--)
#define pii pair<int, int>
#define mp make_pair
#define F first
#define S second
#define PB push_back
#define N 105

using namespace std;

int n;

void answer(vector<char> s) {
    cout <<"! ";
    for (auto c : s) cout <<c;
    cout <<endl;
    exit(0);
}
void ask(int u, int v) {
    cout <<"? "<<u<<' '<<v<<endl;
}

int main() {

    cin >> n;
    vector<char> res(n, 0);

    if (n <= 3) {
        FOR(i, 1, n) {
            ask(i, i);
            char c; cin >> c;
            res[i-1] = c;
        }
        answer(res);
    }

    vector<int> cnt[n+1];
    FOR(i, 1, n) cnt[i].resize(27);
    int mid = (n+1)/2;
    ask(1, mid);
    FOR(i, 1, mid*(mid+1)/2) {
        string s; cin >> s;
        for (auto c : s) cnt[s.length()][c-'a']++;
    }
    ask(1, mid-1);
    FOR(i, 1, (mid-1)*mid/2) {
        string s; cin >> s;
        for (auto c : s) cnt[s.length()][c-'a']--;
    }
    FORD(i, mid, 1) {
        char tmp = 'a';
        FOR(c, 'a', 'z')
            if (cnt[mid-i+1][c-'a'] > 0) tmp = c;
        res[i-1] = tmp;
        FOR(len, mid-i+2, mid)
            cnt[len][tmp-'a']--;
    }
    FOR(i, 1, n) {
        cnt[i].resize(0);
        cnt[i].resize(27);
    }
    //answer(res);
    ask(1, n);
    FOR(i, 1, n*(n+1)/2) {
        string s; cin >> s;
        for (auto c : s) cnt[s.length()][c-'a']++;
    }
    FOR(c, 'a', 'z') {
        int curCnt = 0;
        FORD(i, mid-1, 1) {
            int val = cnt[i+1][c-'a'] - cnt[i][c-'a'] - curCnt;
            int pos1 = i+1;
            int pos2 = n-i;
            if (val == 2 || (val == 1 && res[pos1-1] != c)) res[pos2-1] = c;
            curCnt += val;
        }
    }
    FOR(i, 0, n-2) cnt[1][res[i]-'a']--;
    FOR(c, 'a', 'z')
        if (cnt[1][c-'a']) res[n-1] = c;
    answer(res);
}


#include <bits/stdc++.h>
#define FOR(i, u, v) for (int i = u; i <= v; i++)
#define FORD(i, v, u) for (int i = v; i >= u; i--)
#define pii pair<int, int>
#define mp make_pair
#define F first
#define S second
#define PB push_back
#define N 105

using namespace std;

int n;

void answer(vector<char> s) {
    cout <<"! ";
    for (auto c : s) cout <<c;
    cout <<endl;
    exit(0);
}
void ask(int u, int v) {
    cout <<"? "<<u<<' '<<v<<endl;
}

int main() {

    cin >> n;
    vector<char> res(n, 0);

    if (n <= 3) {
        FOR(i, 1, n) {
            ask(i, i);
            char c; cin >> c;
            res[i-1] = c;
        }
        answer(res);
    }

    vector<int> cnt[n+1];
    FOR(i, 1, n) cnt[i].resize(27);
    int mid = (n+1)/2;
    ask(1, mid);
    FOR(i, 1, mid*(mid+1)/2) {
        string s; cin >> s;
        for (auto c : s) cnt[s.length()][c-'a']++;
    }
    ask(1, mid-1);
    FOR(i, 1, (mid-1)*mid/2) {
        string s; cin >> s;
        for (auto c : s) cnt[s.length()][c-'a']--;
    }
    FORD(i, mid, 1) {
        char tmp = 'a';
        FOR(c, 'a', 'z')
            if (cnt[mid-i+1][c-'a'] > 0) tmp = c;
        res[i-1] = tmp;
        FOR(len, mid-i+2, mid)
            cnt[len][tmp-'a']--;
    }
    FOR(i, 1, n) {
        cnt[i].resize(0);
        cnt[i].resize(27);
    }
    //answer(res);
    ask(1, n);
    FOR(i, 1, n*(n+1)/2) {
        string s; cin >> s;
        for (auto c : s) cnt[s.length()][c-'a']++;
    }
    FOR(c, 'a', 'z') {
        int curCnt = 0;
        FORD(i, mid-1, 1) {
            int val = cnt[i+1][c-'a'] - cnt[i][c-'a'] - curCnt;
            int pos1 = i+1;
            int pos2 = n-i;
            if (val == 2 || (val == 1 && res[pos1-1] != c)) res[pos2-1] = c;
            curCnt = val;
        }
    }
    FOR(i, 0, n-2) cnt[1][res[i]-'a']--;
    FOR(c, 'a', 'z')
        if (cnt[1][c-'a']) res[n-1] = c;
    answer(res);
}


#define _USE_MATH_DEFINES
#include <iostream>
#include <cassert>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <cstdio>
#include <complex>
#include <numeric>
#include <string.h>
#include <random>
#include <cmath>
#define rep(i,n) for (int i = 0; i < (int)n; i++)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pi;
typedef pair<pi, pi> pp;
typedef pair<ll, ll> pl;
typedef pair<int, string> is;
double PI = 3.141592653589793238462643383279;
const double EPS = 1e-9;
const ll MOD = 1000000007;
//const ll MOD = 998244353;
const int inf = 1 << 30;
const ll linf = 1LL << 60;

int n;
map<string, int> m;

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cin >> n;
    if (n == 1) {
        cout << "? 1 1" << endl;
        string str;
        cin >> str;
        cout << "! " << str << endl;
        return 0;
    }
    if (n == 2) {
        cout << "? 1 1" << endl;
        string str1;
        cin >> str1;
        cout << "? 2 2" << endl;
        string str2;
        cin >> str2;
        cout << "! " << str1 << str2 << endl;
        return 0;
    }
    int mid = (n+1)/2;
    cout << "? 1 " << mid << endl;
    rep(i,(mid+1)*mid/2) {
        string str;
        cin >> str;
        sort(str.begin(), str.end());
        m[str]++;
    }
    cout << "? 2 " << mid << endl;
    rep(i,(mid-1)*mid/2) {
        string str;
        cin >> str;
        sort(str.begin(), str.end());
        m[str]--;
    }
    string ans(n, '-');
    vector<is> v;
    int pos = 0;
    for (auto it = m.begin(); it != m.end(); it++) {
        if ((*it).second == 0) continue;
        v.push_back(is((int)((*it).first.size()), (*it).first));
    }
    sort(v.begin(), v.end());
    ans[0] = v[0].second[0];
    for (int i = 1; i < mid; i++) {
        int cn[26] = {0};
        for (char c : v[i-1].second) cn[c-'a']--;
        for (char c : v[i].second) cn[c-'a']++;
        rep(j,26) {
            if (cn[j]) {
                ans[i] = 'a'+j;
            }
        }
    }

    cout << "? 1 " << n << endl;
    int hoge[26][101];
    memset(hoge, 0, sizeof(hoge));
    rep(i,(n+1)*n/2) {
        string str;
        cin >> str;
        rep(j,str.size()) {
            hoge[str[j]-'a'][str.size()]++;
        }
    }
    int huga[26][101];
    memset(huga, 0, sizeof(huga));
    for (int i = 1; i <= n-mid; i++) {
        rep(j,26) {
            int c = hoge[j][1] - (hoge[j][i+1]-hoge[j][i]);
            rep(k,i) if (ans[k] == 'a'+j) c--;
            rep(k,i) if (ans[n-1-k] == 'a'+j) c--;
            if (c) ans[n-i] = 'a'+j;
        }
    }

    cout << "! " << ans << endl;
}


#include <bits/stdc++.h>

#define ld long double
#define sf scanf
#define pf printf
#define pb push_back
#define PI ( acos(-1.0) )
#define IN freopen("input.txt","r",stdin)
#define OUT freopen("output.txt","w",stdout)
#define FOR(i,a,b) for(int i=a ; i<=b ; i++)
#define FORD(i,a,b) for(int i=a ; i>=b ; i--)
#define INF 1000000000
#define ll long long int
#define eps (1e-8)
#define sq(x) ( (x)*(x) )
#define all(x) x.begin(),x.end()
#define flog2(n) 64 - __builtin_clzll(n) - 1
#define popcnt(n) __builtin_popcountll(n)

using namespace std;

typedef pair < int, int > pii;
typedef pair < ll, ll > pll;

vector<string> ask(int l, int r) {
    cout << "? " << l << " " << r << endl;
    vector<string> res;
    string s;
    int m = (r - l + 1) * (r - l + 2) / 2;
    FOR (i, 1, m) {
        cin >> s;
        res.pb(s);
    }
    return res;
}

vector<char> big[105][3];

void process(vector<string>& v, int n, int t) {
    vector<string> bucket[105];
    for (auto &s: v) {
        bucket[s.size()].pb(s);
    }

    vector<char> res[105];
    multiset<char> remain;
    for (auto i: bucket[1]) remain.insert(i[0]);
    int pos = 0;
    FORD (i, n - 1, 1) {
        pos++;
        if (remain.size() <= 2) {
            for (auto j: remain) {
                res[pos].pb(j);
            }
            break;
        }
        vector<int> cnt(27), cnt1(27);
        for (auto &s: bucket[i]) {
            for (auto j: s) {
                cnt[j - 'a']++;
            }
        }
        FOR (j, 1, pos - 1) {
            for (auto k: res[j]) {
                cnt[k - 'a'] -= min(j, i);
            }
        }
        for (auto c: remain) {
            cnt1[c - 'a'] += n + 1 - i;
        }

        FOR (j, 0, 25) {
            if (cnt[j] != cnt1[j]) {
                res[pos].pb(j + 'a');
                // cout << j << " ";
            }
        }
        if (res[pos].size() == 1) {
            res[pos].pb(res[pos][0]);
        }
        for (auto j: res[pos]) {
            remain.erase(remain.find(j));
        }

    }
    if (n == 1) {
        res[1].pb(v[0][0]);
    }
    FOR (i, 1, n) {
        // for (auto j: res[i]) cout << j << " "; cout << endl;
        big[i][t] = res[i];
    }
}

char res[105];

char rest(vector<char>& v, char c) {
    if (v.size() == 1) return c;
    if (v[0] == c) return v[1];
    return v[0];
}

// get from 1 to m
void getAll(int m) {
    vector<string> v1 = ask(1, m);
    if (m == 1) {
        res[1] = v1[0][0];
        return;
    }
    process(v1, m, 1);
    vector<string> v2 = ask(1, m - 1);
    process(v2, m - 1, 2);
    vector<int> cnt(26);
    FOR (i, 1, m) {
        for (auto j: big[i][1]) {
            cnt[j - 'a']++;
        }
        for (auto j: big[i][2]) {
            cnt[j - 'a']--;
        }
    }
    FOR (i, 0, 25) {
        if (cnt[i]) {
            res[m] = i + 'a';
            break;
        }
    }
    vector<int> num;
    vector<vector<char>> charSet;

    int j = m - 1;
    int i = 1;
    while(1) {
        num.pb(i);
        if (j > i) num.pb(j);
        i++; j--;
        if (i > j) break;
    }
    char last = res[m];
    FOR (i, 1, m) {
        charSet.pb(big[i][1]);
        charSet.pb(big[i][2]);
    }

    FOR (i, 0, m - 2) {
        // cout << num[i] << " " << last << " - ";
        // for (auto j: charSet[i]) cout << j << " ";
        // cout << endl;
        res[num[i]] = rest(charSet[i], last);
        last = res[num[i]];
    }
}

int main()
{//IN; //OUT;
    ios::sync_with_stdio(0);
    cin.tie(NULL);

    int n;
    cin >> n;

    vector<string> a = ask(1, n);
    if (n == 1) {
        cout << "! " << a[0][0] << endl;
        return 0;
    }

    process(a, n, 0);
    int mid = n / 2;
    getAll(mid);
    FOR (i, 1, mid) {
        res[n + 1 - i] = rest(big[i][0], res[i]);
    }

    if (n % 2) {
        res[mid + 1] = big[mid + 1][0][0];
    }
    cout << "! ";
    FOR (i, 1, n) cout << (char) res[i];
    cout << endl;

    return 0;
}


#include <bits/stdc++.h>

#define ld long double
#define sf scanf
#define pf printf
#define pb push_back
#define PI ( acos(-1.0) )
#define IN freopen("input.txt","r",stdin)
#define OUT freopen("output.txt","w",stdout)
#define FOR(i,a,b) for(int i=a ; i<=b ; i++)
#define FORD(i,a,b) for(int i=a ; i>=b ; i--)
#define INF 1000000000
#define ll long long int
#define eps (1e-8)
#define sq(x) ( (x)*(x) )
#define all(x) x.begin(),x.end()
#define flog2(n) 64 - __builtin_clzll(n) - 1
#define popcnt(n) __builtin_popcountll(n)

using namespace std;

typedef pair < int, int > pii;
typedef pair < ll, ll > pll;

vector<string> ask(int l, int r) {
    cout << l << " " << r << endl;
    int m;
    vector<string> res;
    cin >> m;
    FOR (i, 1, m) {
        string s;
        cin >> s;
        res.pb(s);
    }
    return res;
}

vector<char> big[105][3];

void process(vector<string>& v, int n, int t) {
    vector<string> bucket[105];
    for (auto &s: v) {
        bucket[s.size()].pb(s);
    }

    vector<char> res[105];
    multiset<char> remain;
    for (auto i: bucket[1]) remain.insert(i[0]);
    int pos = 0;
    FORD (i, n - 1, 1) {
        pos++;
        if (remain.size() <= 2) {
            for (auto j: remain) {
                res[pos].pb(j);
            }
            break;
        }
        vector<int> cnt(27), cnt1(27);
        for (auto &s: bucket[i]) {
            for (auto j: s) {
                cnt[j - 'a']++;
            }
        }
        FOR (j, 1, pos - 1) {
            for (auto k: res[j]) {
                cnt[k - 'a'] -= min(j, i);
            }
        }
        for (auto c: remain) {
            cnt1[c - 'a'] += n + 1 - i;
        }

        FOR (j, 0, 25) {
            if (cnt[j] != cnt1[j]) {
                res[pos].pb(j + 'a');
                // cout << j << " ";
            }
        }
        if (res[pos].size() == 1) {
            res[pos].pb(res[pos][0]);
        }
        for (auto j: res[pos]) {
            remain.erase(remain.find(j));
        }

    }
    if (n == 1) {
        res[1].pb(v[0][0]);
    }
    FOR (i, 1, n) {
        // for (auto j: res[i]) cout << j << " "; cout << endl;
        big[i][t] = res[i];
    }
}

char res[105];

char rest(vector<char>& v, char c) {
    if (v.size() == 1) return c;
    if (v[0] == c) return v[1];
    return v[0];
}

// get from 1 to m
void getAll(int m) {
    vector<string> v1 = ask(1, m);
    if (m == 1) {
        res[1] = v1[0][0];
        return;
    }
    process(v1, m, 1);
    vector<string> v2 = ask(1, m - 1);
    process(v2, m - 1, 2);
    vector<int> cnt(26);
    FOR (i, 1, m) {
        for (auto j: big[i][1]) {
            cnt[j - 'a']++;
        }
        for (auto j: big[i][2]) {
            cnt[j - 'a']--;
        }
    }
    FOR (i, 0, 25) {
        if (cnt[i]) {
            res[m] = i + 'a';
            break;
        }
    }
    vector<int> num;
    vector<vector<char>> charSet;

    int j = m - 1;
    int i = 1;
    while(1) {
        num.pb(i);
        if (j > i) num.pb(j);
        i++; j--;
        if (i > j) break;
    }
    char last = res[m];
    FOR (i, 1, m) {
        charSet.pb(big[i][1]);
        charSet.pb(big[i][2]);
    }

    FOR (i, 0, m - 2) {
        // cout << num[i] << " " << last << " - ";
        // for (auto j: charSet[i]) cout << j << " ";
        // cout << endl;
        res[num[i]] = rest(charSet[i], last);
        last = res[num[i]];
    }
}

int main()
{//IN; //OUT;
    ios::sync_with_stdio(0);
    cin.tie(NULL);

    int n;
    cin >> n;

    vector<string> a = ask(1, n);
    if (n == 1) {
        cout << "! " << a[0][0] << endl;
        return 0;
    }

    process(a, n, 0);
    int mid = n / 2;
    getAll(mid);
    FOR (i, 1, mid) {
        res[n + 1 - i] = rest(big[i][0], res[i]);
    }

    if (n % 2) {
        res[mid + 1] = big[mid + 1][0][0];
    }
    cout << "! ";
    FOR (i, 1, n) cout << (char) res[i];
    cout << endl;

    return 0;
}


#include<bits/stdc++.h>
#define LL long long
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
using namespace std;
inline int read(){
	int v=0,f=1;
	char c=getchar();
	while (c<'0' || c>'9'){
		if (c=='-') f=-1;
		c=getchar();
	}
	while (c>='0' && c<='9'){
		v=v*10+c-'0';
		c=getchar();
	}
	return v*f;
}
const int Maxn=105;
int cnt[Maxn][26];
int S1[Maxn],S2[Maxn],S3[Maxn];
char tmp[Maxn];
void Query(int l,int r){
	printf("? %d %d\n",l,r);
	fflush(stdout);
	memset(cnt,0,sizeof(cnt));
	int c=(r-l+1)*(r-l+2)/2;
	for (int i=0;i<c;i++){
		scanf("%s",tmp);
		int l=strlen(tmp);
		for (int j=0;j<l;j++){
			cnt[l][tmp[j]-'a']++;
		}
	}
}
int ans[Maxn];
vector<pair<int,int> > G[Maxn];
bool vis[Maxn];
int main(){
	int n;
	scanf("%d",&n);
	if (n==1){
		Query(1,1);
		for (int i=0;i<26;i++){
			if(cnt[1][i]){
				printf("! %c\n",i+'a');
				fflush(stdout);
				return 0;
			}
		}
	}
	int l=1,r=n,mid=l+r>>1;
	vector<int> V;
	if (mid-l==r-mid){
		Query(1,n);
		int T=(n+1)/2;
		for (int i=T;i>=1;i--){
			for (int j=0;j<26;j++){
				S1[i]+=cnt[i][j]*j;
				S1[i]-=cnt[i-1][j]*j;
			}
			for (int j=i+1;j<=T;j++){
				S1[i]-=S1[j];
			}
		}
		for (int i=1;i<=T;i++){
			int x=i,y=n+1-i;
			if (x==y){
				ans[x]=S1[i];
				V.pb(x);
			}
			else{
				G[x].pb(mp(y,S1[i]));
				G[y].pb(mp(x,S1[i]));
			}
		}
		Query(1,mid-1);
		T=mid/2;
		for (int i=T;i>=1;i--){
			for (int j=0;j<26;j++){
				S2[i]+=cnt[i][j]*j;
				S2[i]-=cnt[i-1][j]*j;
			}
			for (int j=i+1;j<=T;j++){
				S2[i]-=S2[j];
			}
		}
		for (int i=1;i<=T;i++){
			int x=i,y=mid-i;
			if (x==y){
				ans[x]=S2[i];
				V.pb(x);
			}
			else{
				G[x].pb(mp(y,S2[i]));
				G[y].pb(mp(x,S2[i]));
			}
		}
		Query(mid,n);
		T=(n-mid+2)/2;
		for (int i=T;i>=1;i--){
			for (int j=0;j<26;j++){
				S3[i]+=cnt[i][j]*j;
				S3[i]-=cnt[i-1][j]*j;
			}
			for (int j=i+1;j<=T;j++){
				S3[i]-=S3[j];
			}
		}
		for (int i=1;i<=T;i++){
			int x=i+mid-1,y=mid+n-x;
			if (x==y){
				ans[x]=S3[i];
				V.pb(x);
			}
			else{
				G[x].pb(mp(y,S3[i]));
				G[y].pb(mp(x,S3[i]));
			}
		}
		while (!V.empty()){
			int x=V.back();V.pop_back();
			if (vis[x]) continue;
			vis[x]=true;
			for (int i=0;i<G[x].size();i++){
				int A=G[x][i].first,S=G[x][i].second;
				ans[A]=S-ans[x];
				V.pb(A);
			}
		}
		printf("! ");
		for (int i=1;i<=n;i++){
			putchar(ans[i]+'a');
		}
		printf("\n");
		fflush(stdout);
		return 0;
	}

	Query(1,n);
	int T=(n+1)/2;
	for (int i=T;i>=1;i--){
		for (int j=0;j<26;j++){
			S1[i]+=cnt[i][j]*j;
			S1[i]-=cnt[i-1][j]*j;
		}
		for (int j=i+1;j<=T;j++){
			S1[i]-=S1[j];
		}
	}
	for (int i=1;i<=T;i++){
		int x=i,y=n+1-i;
		if (x==y){
			ans[x]=S1[i];
			V.pb(x);
		}
		else{
			G[x].pb(mp(y,S1[i]));
			G[y].pb(mp(x,S1[i]));
		}
	}
	Query(1,mid);
	T=(mid+1)/2;
	for (int i=T;i>=1;i--){
		for (int j=0;j<26;j++){
			S2[i]+=cnt[i][j]*j;
			S2[i]-=cnt[i-1][j]*j;
		}
		for (int j=i+1;j<=T;j++){
			S2[i]-=S2[j];
		}
	}
	for (int i=1;i<=T;i++){
		int x=i,y=mid+1-i;
		if (x==y){
			ans[x]=S2[i];
			V.pb(x);
		}
		else{
			G[x].pb(mp(y,S2[i]));
			G[y].pb(mp(x,S2[i]));
		}
	}
	Query(mid,n);
	T=(n-mid+2)/2;
	for (int i=T;i>=1;i--){
		for (int j=0;j<26;j++){
			S3[i]+=cnt[i][j]*j;
			S3[i]-=cnt[i-1][j]*j;
		}
		for (int j=i+1;j<=T;j++){
			S3[i]-=S3[j];
		}
	}
	for (int i=1;i<=T;i++){
		int x=i+mid-1,y=mid+n-x;
		if (x==y){
			ans[x]=S3[i];
			V.pb(x);
		}
		else{
			G[x].pb(mp(y,S3[i]));
			G[y].pb(mp(x,S3[i]));
		}
	}
	while (!V.empty()){
		int x=V.back();V.pop_back();
		if (vis[x]) continue;
		vis[x]=true;
		for (int i=0;i<G[x].size();i++){
			int A=G[x][i].first,S=G[x][i].second;
			ans[A]=S-ans[x];
			V.pb(A);
		}
	}
	printf("! ");
	for (int i=1;i<=n;i++){
		putchar(ans[i]+'a');
	}
	printf("\n");
	fflush(stdout);
	return 0;
}