Cipher
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 319 Accepted Submission(s): 141
The length of the message is always less or equal than n. If the message is shorter than n, then spaces are added to the end of the message to get the message with the length n.
Help Alice and Bob and write program which reads the key and then a sequence of pairs consisting of k and message to be encoded k times and produces a list of encoded messages.
10 4 5 3 7 2 8 1 6 10 9 1 Hello Bob 1995 CERC 0 0
BolHeol b C RCE
题目大意
Bob和Alice想用一种新的加密方式,他们编码和译码都是依靠秘密钥匙,秘密钥匙选用一串数字a1,……an(0<ai<=n),编码要求:将信息(长度不大于n,如果小于n请自己补上空格)中i位置上的字符放到ai位置上。这个过程经过K遍之后就成了加密后的信息。现在要求你写一个程序帮他们实现编码。
题目分析
拿到这个题可能不知道如何下手,唯一能想到的就是直接模拟。但有没有注意到他没有给你k的范围。如果k很大,模拟的结果就是TLE,所以这个方式是行不通的。有没有注意到他是将i位置上的字符放在ai位置上,可以想到用置换群做。以sample的数据为例:
i: 1 2 3 4 5 6 7 8 9 10
ai: 4 5 3 7 2 8 1 6 10 9
信息: H e l l o B o b
我们先模拟一下:
1次 i: 1 2 3 4 5 6 7 8 9 10
信息 : B o l H e o l b
2次 i: 1 2 3 4 5 6 7 8 9 10
信息 : l e l B o H o b
3次 i: 1 2 3 4 5 6 7 8 9 10
信息 : H o l l e o B b
不知道大家发现没有在交换过程中,位置i为1,4,7的轮回,2和5交换,3不动,6和8换,9和10换。在看看我们的秘密钥匙:
i: 1 2 3 4 5 6 7 8 9 10
ai: 4 5 3 7 2 8 1 6 10 9
把它看成一个置换群,则可以找出循环(轮换):
(1 4 7)(2 5)(3)(6 8)(9 10)
这和我们上面找出的规律是否有一定联系呢?
每次操作其实都是将这个循环右移,当操作循环长度L次时回到原来状态。
那么我们只要做k%L次就够了,但如果还是用模拟还是不行的,我们要运到群换群的幂,关于幂运算,具体看潘震皓的论文。这里我只是提一下他的算法实现:
l For 源置换中每一个循环
n For 环中每一个未标记元素
u Do
l 做上标记
l 放入结果数组
l 前进k格
u Until 回到这个元素
u 将结果数组中的元素取出,得到的环,便是目标置换包含的一个循环
兔子
#define DeBUG
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <string>
#include <set>
#include <sstream>
#include <map>
#include <list>
#include <bitset>
using namespace std ;
#define zero {0}
#define INF 0x3f3f3f3f
#define EPS 1e-6
#define TRUE true
#define FALSE false
typedef long long LL;
const double PI = acos(-1.0);
//#pragma comment(linker, "/STACK:102400000,102400000")
inline int sgn(double x)
{
return fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1);
}
#define N 1100
char input[N], output[N];
//计算循环群
int num[N], array[N][N], cycle[N];
//初始数组,循环hash数组,循环次数
void func(const int &n)
{
memset(cycle, 0, sizeof(cycle));
for (int i = 0; i < n; i++)
array[0][i] = i;
int tmp = n;
int tm = 1;//循环次数
while (tmp)
{
for (int i = 0; i < n; i++)
if (!cycle[i])//没有完成计算循环进行hash操作
array[tm][num[i]] = array[tm - 1][i];
for (int i = 0; i < n; i++)//完成循环记录
if (!cycle[i] && array[tm][i] == array[0][i])
{
cycle[i] = tm;
--tmp;
}
++tm;//循环+1
}
}
int main()
{
#ifdef DeBUGs
freopen("C:\\Users\\Sky\\Desktop\\1.in", "r", stdin);
#endif
int n, k;
while (scanf("%d", &n), n)
{
for (int i = 0; i < n; i++)
{
scanf("%d", &num[i]);
--num[i];
}
func(n);
while (scanf("%d", &k), k)
{
getchar();
gets(input);
for (int i = strlen(input); i < n; i++)
input[i] = ' ';
memset(output, ' ', sizeof(output));
for (int i = 0; i < n; i++)
{
output[i] = input[array[k % cycle[i]][i]];
//次数对循环取模得到对应变量,分循环群
}
output[n] = '\0';
puts(output);
}
printf("\n");
}
return 0;
}