题意整理

  • 给定一种加密算法,通过算法将输入字符串进行加密处理。
  • 加密算法需要先通过密钥绘制密码表,然后通过密码表对输入字符串加密。
  • 密码表是一个大小为的表格,从前往后一次由密钥中的字母组成,遇到重复的字母,则跳过,如果密钥中的字母用完了,则取标准字母表中的字母。如果遇到字母j,则按照字母i进行处理。
  • 得到密码表之后,按输入字符串,每隔两个字母进行一次加密。假设待处理的两个字母是,如果相等,则直接写入;如果在同一行,将紧靠右端字母写入;如果在同一列,将紧靠下方字母写入;如果不同行且不同列,写入矩阵中与同行的另外两角。

方法一(模拟)

1.解题思路

  • 首先通过密钥获取密码表。
  • 遍历整个str字符串,每次得到当前相邻的两个字符,通过密码表,获取位置信息。根据加密规则进行加密处理。
  • 如果字符串长度为偶数,直接加密完成,如果是奇数,需要处理掉最后一个字符(直接写入密文即可)。

动图展示:
图片说明

2.代码实现

import java.util.*;

public class Solution {
    /**
     * playfair加密算法
     * @param key string字符串 密钥
     * @param str string字符串 明文
     * @return string字符串
     */
    public String Encode (String key, String str) {
        //获取密码表
        char[][] table=getTable(key);
        int n=str.length();
        //初始化结果
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<=n-2;i+=2){
            //如果是'j',则替换为'i'
            char p1=str.charAt(i);
            if(p1=='j') p1='i';
            char p2=str.charAt(i+1);
            if(p2=='j') p2='i';
            //获取位置信息
            int[] point1=getPoint(p1,table);
            int[] point2=getPoint(p2,table);
            int row1=point1[0];
            int col1=point1[1];
            int row2=point2[0];
            int col2=point2[1];
            //如果是相同的两个字母,直接写入
            if(p1==p2){
                sb.append(p1).append(p2);
            }
            else{
                //如果是同一行,将紧靠p1p2右端字母写入
                if(row1==row2){
                    char c1=table[row1][(col1+1)%5];
                    char c2=table[row2][(col2+1)%5];
                    sb.append(c1).append(c2);
                }
                //如果是同一列,将紧靠p1p2下方字母写入
                else if(col1==col2){
                    char c1=table[(row1+1)%5][col1];
                    char c2=table[(row2+1)%5][col2];
                    sb.append(c1).append(c2);
                }
                //如果不同行且不同列,写入矩阵中与p1p2同行的另外两角
                else{
                    char c1=table[row1][col2];
                    char c2=table[row2][col1];
                    sb.append(c1).append(c2);
                }
            }
        }

        //如果n是奇数,还需写入最后一个字符
        if(n%2==1){
            sb.append(str.charAt(n-1));
        }
        return sb.toString();
    }

    //绘制密码表
    private char[][] getTable(String key){
        char[][] arr=new char[5][5];
        boolean[] f=new boolean[26];
        key=key+"abcdefghiklmnopqrstuvwxyz";
        int index=0;
        for(int i=0;i<5;i++){
            for(int j=0;j<5;){
                char c=key.charAt(index++);
                if(c=='j'){
                    c='i';
                }
                //只有字母c未被标记,才写入密码表
                if(!f[c-'a']){
                    f[c-'a']=true;
                    arr[i][j++]=c;
                }
            }
        }
        return arr;
    }

    //从密码表获取当前字符的位置信息
    private int[] getPoint(char c,char[][] table){
        for(int i=0;i<5;i++){
            for(int j=0;j<5;j++){
                if(table[i][j]==c){
                    return new int[]{i,j};
                }
            }
        }
        return null;
    }
}

3.复杂度分析

  • 时间复杂度:绘制密码表和通过密码表获取字符位置都只需要常数次操作,另外需要遍历整个str字符串,所以时间复杂度为
  • 空间复杂度:需要额外常数级别的空间,所以空间复杂度为

方法二(模拟+哈希表)

1.解题思路

思路与方法一大致相同,只是利用哈希表来获取字符在密码表中的位置,一定程度上减少了操作次数。原来最多需要循环次才能找到位置,现在只需获取字符键对应的值,即可直接找到位置。

2.代码实现

import java.util.*;

public class Solution {
    /**
     * playfair加密算法
     * @param key string字符串 密钥
     * @param str string字符串 明文
     * @return string字符串
     */
    //初始化哈希表
    Map<Character,int[]> map=new HashMap<>();

    public String Encode (String key, String str) {
        //获取密码表
        char[][] table=getTable(key);
        int n=str.length();
        //初始化结果
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<=n-2;i+=2){
            //如果是'j',则替换为'i'
            char p1=str.charAt(i);
            if(p1=='j') p1='i';
            char p2=str.charAt(i+1);
            if(p2=='j') p2='i';
            //根据键值获取位置信息
            int[] point1=map.get(p1);
            int[] point2=map.get(p2);
            int row1=point1[0];
            int col1=point1[1];
            int row2=point2[0];
            int col2=point2[1];
            //如果是相同的两个字母,直接写入
            if(p1==p2){
                sb.append(p1).append(p2);
            }
            else{
                //如果是同一行,将紧靠p1p2右端字母写入
                if(row1==row2){
                    char c1=table[row1][(col1+1)%5];
                    char c2=table[row2][(col2+1)%5];
                    sb.append(c1).append(c2);
                }
                //如果是同一列,将紧靠p1p2下方字母写入
                else if(col1==col2){
                    char c1=table[(row1+1)%5][col1];
                    char c2=table[(row2+1)%5][col2];
                    sb.append(c1).append(c2);
                }
                //如果不同行且不同列,写入矩阵中与p1p2同行的另外两角
                else{
                    char c1=table[row1][col2];
                    char c2=table[row2][col1];
                    sb.append(c1).append(c2);
                }
            }
        }

        //如果n是奇数,还需写入最后一个字符
        if(n%2==1){
            sb.append(str.charAt(n-1));
        }
        return sb.toString();
    }

    //绘制密码表
    private char[][] getTable(String key){
        char[][] arr=new char[5][5];
        boolean[] f=new boolean[26];
        key=key+"abcdefghiklmnopqrstuvwxyz";
        int index=0;
        for(int i=0;i<5;i++){
            for(int j=0;j<5;){
                char c=key.charAt(index++);
                if(c=='j'){
                    c='i';
                }
                //只有字母c未被标记,才写入密码表
                if(!f[c-'a']){
                    f[c-'a']=true;
                    //将位置信息放入哈希表
                    map.put(c,new int[]{i,j});
                    //给密码表赋值
                    arr[i][j++]=c;
                }
            }
        }
        return arr;
    }

    //从密码表获取当前字符的位置信息
    private int[] getPoint(char c,char[][] table){
        for(int i=0;i<5;i++){
            for(int j=0;j<5;j++){
                if(table[i][j]==c){
                    return new int[]{i,j};
                }
            }
        }
        return null;
    }
}

3.复杂度分析

  • 时间复杂度:绘制密码表和通过哈希表获取字符位置都只需要常数次操作,另外需要遍历整个str字符串,所以时间复杂度为
  • 空间复杂度:需要额外常数级别的空间,所以空间复杂度为