/*
* 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;
}
poj 1026 Cipher
最新推荐文章于 2020-03-08 20:38:20 发布