一種Playfair密碼變種加密方法如下:首先選擇一個密鑰單詞(稱爲pair)(字母不重複,且都爲小寫字母),然後與字母表中其他字母一起填入至一個5x5的方陣中,填入方法如下:
1.首先按行填入密鑰串。
2.緊接其後,按字母序按行填入不在密鑰串中的字母。
3.由於方陣中只有25個位置,最後剩下的那個字母則不需變換。
如果密鑰爲youandme,則該方陣如下:
y o u a n
d m e b c
f g h i j
k l p q r
s t v w x
在加密一對字母時,如am,在方陣中找到以這兩個字母爲頂點的矩形(紅色字體):
y o u a n
d m e b c
f g h i j
k l p q r
s t v w x
這對字母的加密字母爲該矩形的另一對頂點,如本例中爲ob。
請設計程序,使用上述方法對輸入串進行加密,並輸出加密後的串。
另外有如下規定:
1、一對一對取字母,如果最後只剩下一個字母,則不變換,直接放入加密串中;
2、如果一對字母中的兩個字母相同,則不變換,直接放入加密串中;
3、如果一對字母中有一個字母不在正方形中,則不變換,直接放入加密串中;
4、如果字母對出現在方陣中的同一行或同一列,如df或hi,則只需簡單對調這兩個字母,即變換爲fd或ih;
5、如果在正方形中能夠找到以字母對爲頂點的矩形,假如字母對爲am,則該矩形的另一對頂點字母中,與a同行的字母應在前面,在上例中應是ob;同樣若待變換的字母對爲ta,則變換後的字母對應爲wo;
6、本程序中輸入串均爲小寫字母,並不含標點、空格或其它字符。
解密方法與加密相同,即對加密後的字符串再加密,將得到原始串。
要求輸入形式如下:
從控制檯輸入兩行字符串,第一行爲密鑰單詞(長度小於等於25),第二行爲待加密字符串(長度小於等於50),兩行字符串末尾都有一個回車換行符,並且兩行字符串均爲小寫字母,不含其它字符。
在標準輸出上輸出加密後的字符串。
例如,若輸入:
youandme
welcometohangzhou
則表示輸入的密鑰單詞爲youandme,形成的正方形如上所示;待加密字符串爲welcometohangzhou。在正方形中可以找到以第一對字母we爲頂點的矩形,對應另一對頂點字母爲vb,因此加密後爲vb,同理可找到與字母對lc,et,oh,ho對應的頂點字母對。而字母對om位於上述正方形中的同一列,所以直接以顛倒這兩個字母來加密,即爲mo,字母對an同理。字母對gz中的z不在上述正方形中,因此原樣放到加密串中。最後剩一個字母u也原樣輸出。
因此輸出的結果爲:
vbrmmomvugnagzguu
分析:
純邏輯題,自頂向下求解。先根據需求定義求解流程(順序),再根據每個流程定義函數(包括功能,參數),最後實現每個函數。
解:
#include <stdio.h>
#include <string.h>
#define KEY_X 5 //方陣行
#define KEY_Y 5 //方陣列
#define MAX_PAIR_NUM KEY_X*KEY_Y //方陣字符數
#define MAX_STR 50 //字串最大字節數
typedef int INT32;
typedef char INT8;
typedef void VOID;
/****************************************************************************
* 函數:CreatKey *
* 參數:pair:密鑰單詞. *
* key:密鑰方陣. *
* keyPos:(字母-'a')在key中位置(距key首地址的位置). *
* 返回值:無. *
* 功能:生成密鑰方陣. *
*****************************************************************************/
VOID CreatKey(INT8 *pair, INT8 key[][KEY_Y], INT8 *keyPos)
{
INT32 i,j,k, np;
INT32 c26, n;
memset(keyPos, -1, 26);
np = strlen(pair);
//每個字母1bit
c26 = 0;
for(i=0; pair[i] != 0; i++)
c26 |= (1<<(pair[i]-'a'));
//生成密鑰方陣和keyPos
k = 0;
for(i=0; i<KEY_X; i++)
for(j=0; j<KEY_Y; j++)
{
n = i*KEY_Y+j;
if(n < np)
{
key[i][j] = pair[n];
keyPos[key[i][j]-'a'] = n;
}
else
{
while(1 == ((c26 >> (k)) & 0x1))
k++;
key[i][j] = k+'a';
keyPos[k] = n;
k++;
}
}
/*
printf("Key:\n");
for(i=0; i<KEY_X; i++)
{
for(j=0; j<KEY_Y; j++)
{
printf("%c ", key[i][j]);
}
printf("\n");
}
printf("KeyPos:\n");
for(i=0; i<26; i++)
{
printf("%c:%d ", i+'a', keyPos[i]);
if(0 == (i+1)%KEY_Y)
printf("\n");
}
*/
}
/****************************************************************************
* 函數:Encrypt *
* 參數:str:待加密字串. *
* enstr:(out)密文. *
* key:密鑰方陣. *
* keyPos:(字母-'a')在key中位置(距key首地址的位置). *
* 返回值:無. *
* 功能:加密字串str,並將密文保存在enstr. *
*****************************************************************************/
VOID Encrypt(INT8 *str, INT8 *enstr, INT8 key[][KEY_Y], INT8 *keyPos)
{
INT8 *p, *q;
INT8 a, b;
INT32 x1,y1,x2,y2;
p = str;
q = enstr;
//一對一對取字母
while(NULL != *(p+1))
{
a = *p++;
b = *p++;
a -= 'a';
b -= 'a';
//如果一對字母中的兩個字母相同,則不變換,直接放入加密串中
//如果一對字母中有一個字母不在正方形中,則不變換,直接放入加密串中;
if(a == b || -1 == keyPos[a] || -1 == keyPos[b])
{
*q++ = a+'a';
*q++ = b+'a';
continue;
}
x1 = keyPos[a]/KEY_Y;
y1 = keyPos[a]%KEY_Y;
x2 = keyPos[b]/KEY_Y;
y2 = keyPos[b]%KEY_Y;
//如果字母對出現在方陣中的同一行或同一列,如df或hi,則只需簡單對調這兩個字母,即變換爲fd或ih;
if(x1 == x2 || y1 == y2)
{
*q++ = b+'a';
*q++ = a+'a';
continue;
}
//如果在正方形中能夠找到以字母對爲頂點的矩形,假如字母對爲am,則該矩形的另一對頂點字母中,與a同行的字母應在前面,在上例中應是ob;同樣若待變換的字母對爲ta,則變換後的字母對應爲wo;
*q++ = key[x1][y2];
*q++ = key[x2][y1];
}
//如果最後只剩下一個字母,則不變換,直接放入加密串中
if(*p)
*q++ = *p;
*q = '\0';
}
INT32 main(INT32 argc, INT32 *argv[])
{
INT8 pair[MAX_PAIR_NUM+1]; //密鑰單詞
INT8 str[MAX_STR+1]; //待加密字串
INT8 enstr[MAX_STR+1]; //加密後字串
INT8 key[KEY_X][KEY_Y]; //密鑰方陣
INT8 keyPos[26]; //(字母-'a')在key中位置(距key首地址的位置)
printf("請輸入密鑰單詞(字母不重複,且都爲小寫字母)...\n");
scanf("%s", pair);
printf("請輸入待加密字串(長度小於等於50)...\n");
scanf("%s", str);
CreatKey(pair, key, keyPos);
Encrypt(str, enstr, key, keyPos);
printf("\n\nEncrypt:\n%s\n", enstr);
Encrypt(enstr, str, key, keyPos);
printf("Deciphering:\n%s\n", str);
return 0;
}