【数学思维】codeforces1117E Decypher the String

本文介绍了一种解决互动编程问题的方法,通过三次询问来恢复一个被未知操作混淆的字符串。利用26进制编码定位法,将每个字符位置转换为三位编码,通过比较询问结果,找出每个字符的原始位置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

E. Decypher the String
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
This is an interactive problem. Remember to flush your output while communicating with the testing program. You may use fflush(stdout) in C++, system.out.flush() in Java, stdout.flush() in Python or flush(output) in Pascal to flush the output. If you use some other programming language, consult its documentation. You may also refer to the guide on interactive problems: https://codeforces.com/blog/entry/45307.

You are given a string t consisting of n lowercase Latin letters. This string was cyphered as follows: initially, the jury had a string s consisting of n lowercase Latin letters. Then they applied a sequence of no more than n (possibly zero) operations. i-th operation is denoted by two integers ai and bi (1≤ai,bi≤n), and means swapping two elements of the string with indices ai and bi. All operations were done in the order they were placed in the sequence. For example, if s is xyz and 2 following operations are performed: a1=1,b1=2; a2=2,b2=3, then after the first operation the current string is yxz, and after the second operation the current string is yzx, so t is yzx.

You are asked to restore the original string s. Unfortunately, you have no information about the operations used in the algorithm (you don’t even know if there were any operations in the sequence). But you may run the same sequence of operations on any string you want, provided that it contains only lowercase Latin letters and its length is n, and get the resulting string after those operations.

Can you guess the original string s asking the testing system to run the sequence of swaps no more than 3 times?

The string s and the sequence of swaps are fixed in each test; the interactor doesn’t try to adapt the test to your solution.

Input
Initially the testing system sends one string t, consisting of lowercase Latin letters (1≤|t|=n≤104).

Output
To give the answer, your program should print one line ! s with a line break in the end. After that, it should flush the output and terminate gracefully.

Interaction
Before giving the answer, you may submit no more than 3 queries. To ask a query, print one line in the following format: ? s′, where s′ should be a string consisting of exaclty n lowercase Latin letters. The line should be ended with a line break character. After submitting a query, flush the output and read the answer to your query — a string t′ consisting of n lowercase Latin letters, which is the result of applying the sequence of swaps to string s′. This string will be given on a separate line ended by a line break character.

If you submit an incorrect query (or ask more than 3 queries), the answer to it will be one string 0. After receiving such an answer, your program should terminate immediately — otherwise you may receive verdict “Runtime error”, “Time limit exceeded” or some other verdict instead of “Wrong answer”.


 KSJ曾经考过我们一个小小的脑筋急转弯。1000瓶酒中有1瓶是毒药,问:最少需要多少只小白鼠才能把毒药检测出来?有的人想出了30只:把1000瓶酒三维101010摆放,十只确定层,十只确定行,十只确定列。实际上运用了进制思想,当然最好的方法是采用二进制,两只确定毒药的末位,两只确定毒药的第二位……,总共20只就可以了(三进制有可能更优,但在1000数字下,三进制需要21只,而更大的进制明显不会更优,另外,进制组合的方式也是有可能更优的,比如个位二进制十位三进制之类的,但经过枚举,在1000数字下也达不到更优)
 说远了,看看本题。相似之处就是这种进制定位法,26^3>10000,所以,我们可以用三个小写字符为下标编码,每个下标必然可以得到唯一的一个三位编码值。如果下标pos的26进制下的值是(XYZ)那么,第一个询问字符串中pos位置为X+‘a’,第二个是Y+‘a’,第三个是Z+‘a’,找到这三个字母变化后的可能位置集合,取交集,则交集必然有且只有唯一元素,也就是pos变换之后所在的位置(如果有两个元素的话,和我们的编码唯一性质是相互矛盾的)
 这题昨天没有现场写出来,相当遗憾了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;

int m0=26,m1=26*26,m2=26*26*26,pos,n,p,q,r;
char s[10005],a[10005],b[10005],ans[10005];
vector<int> V1[26],V2[26],V3[26];
int bo[10005];  //bo用来求三个集合的交

int main()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=0;i<n;i+=m1)
		for(int j=1;j<=m1&&j+i<=n;j++)
			a[i+j]=i/m1+'a';
	printf("? ");
	puts(a+1);
	fflush(stdout);
	scanf("%s",b+1);
	for(int i=1;i<=n;i++)
		V1[b[i]-'a'].push_back(i);
		
	for(int i=0;i<n;i+=m0)
		for(int j=1;j<=m0&&j+i<=n;j++)
			a[i+j]=(i/m0)%m0+'a';
	printf("? ");
	puts(a+1);
	fflush(stdout);
	scanf("%s",b+1);
	for(int i=1;i<=n;i++)
		V2[b[i]-'a'].push_back(i);
		
	for(int i=0;i<n;i++)
		for(int j=1;j<=1&&j+i<=n;j++)
			a[i+j]=i%m0+'a';
	printf("? ");
	puts(a+1);
	fflush(stdout);
	scanf("%s",b+1);
	for(int i=1;i<=n;i++)
		V3[b[i]-'a'].push_back(i);
		
	for(int i=0;i<n;i++)
	{
		p=i/m1;
		q=i%m1/m0;
		r=i%m0;
		for(int j:V1[p])
			bo[j]++;
		for(int j:V2[q])
			bo[j]++;
		for(int j:V3[r])
		{
			bo[j]++;
			if(bo[j]==3)
				ans[i]=s[j];
		}
		for(int j:V1[p])
			bo[j]--;
		for(int j:V2[q])
			bo[j]--;
		for(int j:V3[r])
			bo[j]--;
	}
	printf("! ");
	puts(ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值