[2011山东省第二届ACM大学生程序设计竞赛]——Crack Mathmen

Crack Mathmen

题目:http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2165

Time Limit: 1000ms Memory limit: 65536K 

题目描述
Since mathmen take security very seriously, they communicate in encrypted messages. They cipher their texts in this way: for every characther c in the message, they replace c with f(c) = (the ASCII code of c)n mod 1997 if f(c) < 10, they put two preceding zeros in front of f(c) to make it a three digit number; if 10 <= f(c) < 100, they put one preceding zero in front of f(c) to make it a three digit number.
For example, if they choose n = 2 and the message is "World" (without quotation marks), they encode the message like this:
1. the first character is 'W', and it's ASCII code is 87. Then f(′W′) = 87^2 mod 997 = 590.
2. the second character is 'o', and it's ASCII code is 111. Then f(′o′) = 111^2 mod 997 = 357.
3. the third character is 'r', and it's ASCII code is 114. Then f(′r′) = 114^2 mod 997 = 35. Since 10 <= f(′r′) < 100, they add a 0 in front and make it 035.
4. the forth character is 'l', and it's ASCII code is 108. Then f(′l′) = 108^2 mod 997 = 697.
5. the fifth character is 'd', and it's ASCII code is 100. Then f(′d′) = 100^2 mod 997 = 30. Since 10 <= f(′d′) < 100, they add a 0 in front and make it 030.
6. Hence, the encrypted message is "590357035697030".
One day, an encrypted message a mathman sent was intercepted by the human being. As the cleverest one, could you find out what the plain text (i.e., the message before encryption) was?
输入
The input contains multiple test cases. The first line of the input contains a integer, indicating the number of test cases in the input. The first line of each test case contains a non-negative integer n (n <= 10^9). The second line of each test case contains a string of digits. The length of the string is at most 10^6.
输出
For each test case, output a line containing the plain text. If their are no or more than one possible plain text that can be encrypted as the input, then output "No Solution" (without quotation marks). Since mathmen use only alphebetical letters and digits, you can assume that no characters other than alphebetical letters and digits may occur in the plain text. Print a line between two test cases.
示例输入
3
2
590357035697030
0
001001001001001
1000000000
001001001001001
示例输出
World
No Solution

No Solution


这道题,当时比赛时,昊G在翻译完大部分题目后拉着晨阳G俩孩子默默做了N久也没A出来,后来再讲这道题的时候懊悔不已。。。他们一直用数论的密码学,用RSA加密算法来解决,然后题意也没读太透,以为只有字母。之前应该能A出来的,但是没有加数字,结果。。。

言归正传,昊G说,RSA加密算法分两种,一种是可以写出解密方程的,另一种可能没有解密方程,就用打表来解决。于是乎,他们研究后发现找不出解密方程,所以打表解决。(但是,前文说了,他们没打数字的表,所以WA)。这道题,就是用打表解决。

题意就是  给你一个加过密的字符串,让你解密。

加密的过程是  一个字符比如b,b的ASCII码为98, 98^n%997

如果n为2,98^2%997=631.

所以 所给的加密串中若出现 631 则可翻译为b。

加密串为3的倍数,每三个数代表一个字符。(不足三位数用0补足,因为最后%997所以不会有大于3位数情况)

最终由两种 No Solution

① 前文b对应631,若出现另一个字母也对应631,则有二义性,所以No Solution

②加密串中取出了一组数,假设555,但是555没有对应任何一个字母或者数字,则No Solution


解题步骤:

①收取输入的n和加密串(最多至100万个)

②根据n,做一个字典,讲所有字母和数字都存储于数组中。

(此处,做 x(代表字符ASCII值)^n%997,由两种方法,第一种是吉大的模板(用的二进制方法),还有一种直接int型的。)

③然后翻译加密串。

在第②和③要随时注意 No Solution情况。


#include <iostream>
#include <string.h>
#include <string>
using namespace std;
char tran[1000];
char key[1000001];
char ans[340000];
bool flag;

// 大数取模的二进制方法
// a^b0 mod n
int mod_exp(int a,int b0,int n)
{
    if( a>n )   a%=n;
    int i,d=1,b[35];
    for( i=0;i<35;++i )
    {
        b[i]=b0%2;
        b0 /= 2;
        if( b0==0 ) break;
    }//b[i]b[i-1]...b[0] 为b0的二进制表示
    for( ;i>=0;--i )
    {
        d= (d*d)%n;
        if( b[i]==1 )   d=(d*a)%n;
    }
    return d;
}
/*
int mod_exp(int a,int b,int n)
{
    int ans=1;
    while(b)
    {
        if(b&1)
            ans = (ans * a) % n;
        a = a * a % n;
        b = b >> 1;
    }
    return ans;
}
*/

// 构造字典
void judge_c(int n)
{
    int i,temp;
    for(i=48; i<58; ++i)
    {
        temp=mod_exp(i,n,997);
        if( tran[temp]!=-1 )    return;
        else    tran[temp]=char(i);
    }
    for(i=65; i<91; ++i)
    {
        temp=mod_exp(i,n,997);
        if( tran[temp]!=-1 )    return;
        else    tran[temp]=char(i);
    }
    for(i=97; i<123; ++i)
    {
        temp=mod_exp(i,n,997);
        if( tran[temp]!=-1 )    return;
        else    tran[temp]=char(i);
    }
    flag=1;
    return;
}
// 进行翻译
void find_ans(int len)
{
    int i,sum,js=0;
    for(i=0; i<len; i+=3)
    {
        sum=(key[i]-'0')*100+(key[i+1]-'0')*10+key[i+2]-'0';
        if( tran[sum]==-1 )
        {
            cout<<"No Solution"<<endl;
            return;
        }
        ans[js++]=tran[sum];
    }
    for(i=0; i<js; ++i)
        cout<<ans[i];
    cout<<endl;
}
int main()
{
    int test,n;
    cin>>test;
    while(test--)
    {
        memset(tran,-1,sizeof(tran));
        cin>>n;
        // 取回车
        cin.getline(key,1000000,'\n');
        cin.getline(key,1000000,'\n');

        flag=0;
        judge_c(n);
        if(!flag)
        {
            cout<<"No Solution"<<endl;
            continue;
        }
        else    find_ans(strlen(key));
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值