题目链接

C1: http://codeforces.com/contest/1286/problem/C1
C2: http://codeforces.com/contest/1286/problem/C2

题解

首先考虑C1怎么做: 先询问整个串,再询问前\((n-1)\)个字符。二者相比对,对于每一个\(l=1..n\), 前者都比后者多一个长度为\(l\)的字符串,也就是长度为\(l\)的后缀。求出这些后缀是什么,然后后缀之间互相比对,就可以知道每个位置的字符了。
考虑问一遍整个串。位置\(i\)上的字符在整个串的长度为\(l\)的子串(\(l\gt \frac{n}{2}\))共出现了\(\min(i,n-i+1,n-l+1)\)次。将\(l\)\((l+1)\)作差,那么剩下的就是\([n-l+1,l]\)这段区间内的字符各出现了一次。再将作差后的\(l\)\((l-1)\)作差,剩下的是\(l\)\((n-l+1)\)这两个字符。
但是剩下两个字符非常难以判断。考虑用刚才的方法求出前一半,去掉前一半的影响,就可以通过差出来的字符确定答案了。
总询问次数约\(0.75n^2\).
注意特判\(n=1\).

代码

#include<bits/stdc++.h>
#define llong long long
using namespace std;

inline int read()
{
	int x = 0,f = 1; char ch = getchar();
	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
	return x*f;
}

const int N = 101;
const int S = 26;

char str[N+3];

namespace Subtask
{
	int a[N+3][N+3][S+3],cnta[N+3],b[N+3][N+3][S+3],cntb[N+3];
	bool fa[N+3][N+3],fb[N+3][N+3];
	int cur[S+3];
	char ans[N+3];
	void solve(int n,char ans[])
	{
		cout<<'?'<<' '<<1<<' '<<n-1<<' '<<endl; cout.flush();
		for(int i=1; i<=(n-1)*n/2; i++)
		{
			cin>>str+1; int len = strlen(str+1);
			cnta[len]++; for(int j=1; j<=len; j++) a[len][cnta[len]][str[j]-96]++;
		}
		cout<<'?'<<' '<<1<<' '<<n<<' '<<endl; cout.flush();
		for(int i=1; i<=n*(n+1)/2; i++)
		{
			cin>>str+1; int len = strlen(str+1);
			cntb[len]++; for(int j=1; j<=len; j++) b[len][cntb[len]][str[j]-96]++;
		}
		for(int i=1; i<=n; i++)
		{
			int id = 0;
			for(int k=1; k<=cntb[i]; k++)
			{
				bool found = false;
				for(int j=1; j<=cnta[i]; j++)
				{
					if(fa[i][j]) continue;
					bool same = true;
					for(int s=1; s<=S; s++) {if(a[i][j][s]!=b[i][k][s]) {same = false; break;}}
					if(same) {found = true; fa[i][j] = true; break;}
				}
				if(!found) {id = k; break;}
			}
			for(int s=1; s<=S; s++)
			{
				if(b[i][id][s]!=cur[s]) {cur[s]++; ans[n-i+1] = s+96; break;}
			}
		}
	}
}

int a[N+3][N+3][S+3],cnta[N+3];
int tot[N+3][S+3];
int dif[N+3][S+3];
char ans[N+3];
int n;

int main()
{
	cin>>n;
	if(n==1)
	{
		cout<<'?'<<' '<<1<<' '<<1<<endl; cout.flush();
		cin>>ans;
		cout<<'!'<<' '<<ans<<endl; cout.flush();
		return 0;
	}
	else if(n==2)
	{
		Subtask::solve(2,ans);
		cout<<'!'<<' '<<ans+1<<endl; cout.flush();
		return 0;
	}
	int nn = (n+1)>>1;
	Subtask::solve(nn,ans);
	cout<<'?'<<' '<<1<<' '<<n<<endl; cout.flush();
	for(int i=1; i<=n*(n+1)/2; i++)
	{
		cin>>str+1; int len = strlen(str+1);
		cnta[len]++; for(int j=1; j<=len; j++) a[len][cnta[len]][j] = str[j]-96,tot[len][str[j]-96]++;
	}
	for(int l=nn+1; l<=n; l++)
	{
		for(int i=1; i<=nn; i++)
		{
			tot[l][ans[i]-96] -= min(i,n-l+1);
		}
//		printf("tot[%d]: ",l); for(int j=1; j<=S; j++) printf("%d ",tot[l][j]); puts("");
	}
	for(int l=nn+1; l<=n; l++)
	{
		for(int i=1; i<=S; i++)
		{
			dif[l][i] = tot[l][i]-tot[l+1][i];
			if(dif[l][i]!=dif[l-1][i]) {ans[l] = i+96;}
		}
	}
	cout<<'!'<<' '<<ans+1<<endl; cout.flush();
	return 0;
}