poj 1026 Cipher
来源:互联网 发布:时间矩阵图怎么落实 编辑:程序博客网 时间:2024/04/29 02:38
/* * 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;}