poj 1026 Cipher

 /*
  * poj 1026 Cipher

    题目大意:
        Bob 和 Alice约定了一种加密算法。该算法的密钥是一组选定n个不同的数字,
        {a1,a2...an | 1<=ak<=n}。加密步骤具体如下:

        1、输出密钥序列
        2、在密钥序列下方,对应的输出需要加密的字串
        3、按照上方标号的顺序输出字串即密文

        举例如下:
        4 5 3 7 2 8 1 6 10 9 -- 密钥序列
        H e l l o   B o b    -- 原文
        B o l H e o l      b -- 一次加密后的密文

        密文可以按照上述方式被多次加密。本题就是要求出给定加密次数后的密文是什么样子的?

    解题思路:
        模拟题,找规律。

        1、密文序列决定了每个位置只可能固定出现几个字符,以上例的首字符为例。

           a、第i次密文输出的首字符,必然i-1次密文的第7个位置的字符。
           b、而i-1次密文的第7个位置的字符,必然是i-2次密文第4个位置的字符
           c、而i-2次密文的第4个位置的字符,必然是i-3次密文第1个位置的字符

           至此,我们发现第i次密文输出的首字符与第i-3次密文的首字符是同样的,也就是说
           每加密3次,首字符会重复出现一次。以上首字符的变化可以总结如下:

           i-3  i-2  i-1   i   i+1  i+2  i+3
            1 -> 4 -> 7 -> 1 -> 4 -> 7 -> 1

           以上可以看出,第1、4、7位置处的字符均每3轮重复一次。

         2、其他位置的也可以推出类似的规律,针对每个位置推导出重复周期,即可
            很容易的得到最后的密文是什么样子的

    另注:
        本程序提交结果是797ms,看discuss里说用置换群做,只要16ms,差距啊!

        不过,没听过什么置换群,看到一个哥们如下说:
        <本题用置换群做,单独考虑每个字母n次置换后的位置,建议不懂的同学去看下黑书248页>

        这个置换群应该很NB的样子,去学一下。
 */

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>

namespace {
    using namespace std;

    const int N_MAX = 200;

    int keys[N_MAX+1]; // 密钥存储
    int periods[N_MAX+1]; // 每个位置处的周期值
    int previous[N_MAX+1];// 每个位置对应的前一次位置
}
 
int main()
{
    char message[N_MAX+2]; // 消息
    
    int n, k;
    while (scanf("%d", &n) && n!=0)
    {
        for (int i=1; i<=n; i++)
        {
            scanf("%d", &keys[i]);
            
            previous[keys[i]] = i; // 前一个位置赋值
            periods[i] = 0; // 周期初始化
        }

        for (int i=1; i<=n; i++)
        {
            if (periods[i]!=0) continue; // 处理过,不在处理

            // 计算i位置处字符的周期值
            int p=1, t=previous[i];;
            while (t != i)
            {
                ++p;
                t = previous[t];
            }

            // 循环内的所有元素周期值是一样的
            periods[i]=p;
            t=previous[i];
            while (t != i)
            {
                periods[t] = p;
                t = previous[t];
            }
        }

        // 读入并加密消息
        while (scanf("%d", &k) && k!=0)
        {
            getchar(); // 丢掉首部的空格
            gets(&message[1]); // 读入消息
            for (int i = strlen(&message[1])+1; i<=n; i++)
            {
                message[i] = ' '; // 消息长度不足n时,补齐空格
            }

            int r=0;
            for (int i=1; i<=n; i++)
            {
                r = k%periods[i]; // 计算余数
                
                int t=i;
                for (int j=0; j<r; j++) // 查找r轮前的字符索引
                {
                    t = previous[t];
                }
                printf("%c", message[t]); // 打印
            }
            printf("\n"); // 消息打印完毕,换行
        }

        printf("\n"); // After each block there is one empty line. 
    }
    
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值