简体   繁体   English

在 java 中计算 Caesar Cipher 实现的余数

[英]Calculating remainder for Caesar Cipher implementation in java

I'm working on a HackerRank problem .我正在处理HackerRank 问题 Solution this involves converting a string into the Caesar cipher with k shifts.解决方案涉及将字符串转换为具有k个移位的凯撒密码

Julius Caesar protected his confidential information by encrypting it using a cipher. Julius Caesar 通过使用密码对其进行加密来保护他的机密信息。 Caesar's cipher shifts each letter by a number of letters.凯撒的密码将每个字母移动多个字母。 If the shift takes you past the end of the alphabet, just rotate back to the front of the alphabet.如果移位使您越过字母表的末尾,只需旋转回字母表的前面即可。 In the case of a rotation by 3, w, x, y and z would map to z, a, b and c.在旋转 3 的情况下,w、x、y 和 z 将从 map 变为 z、a、b 和 c。

 Original alphabet: abcdefghijklmnopqrstuvwxyz Alphabet rotated +3: defghijklmnopqrstuvwxyzabc

Example:例子:

s = "There's-a-starman-wayting-in-the-sky" s = "There's-a-starman-wayting-in-the-sky"

k = 3 k = 3

The encrypted string: "Wkhuh'v-d-vwdupdq-zdlwlqj-lq-wkh-vnb"加密字符串: "Wkhuh'v-d-vwdupdq-zdlwlqj-lq-wkh-vnb"

Note: The cipher only encrypts letters;注意:密码加密字母; symbols, such as - , remain unencrypted.诸如-之类的符号保持未加密状态。

Function Description: Function 说明:

string s : clear text - a valid ASCII string without any spaces; string s : 明文 - 没有任何空格的有效 ASCII 字符串; int k : the alphabet rotation factor. int k :字母旋转因子。

Constraints:约束:

 1 <= n <= 100 0 <= k <= 100

I need some help to understand where the issue with my code is rooted.我需要一些帮助来了解我的代码问题的根源。

My guess is that it might reside in the lines responsible for handling upper case and lower case letters.我的猜测是它可能驻留在负责处理大写和小写字母的行中。

Lower case letters小写字母

int remainder = (asciiArr[i] + k) % UPPER_BOUND_LOWERCASE;

The upperbound (the highest ascii int value) for lower case character is z which is equal to the character z which is 122.小写字符的上限(最高 ascii int 值)是 z,它等于字符 z,即 122。

Now lets consider a case where my code works.现在让我们考虑一个我的代码可以工作的情况。

Lets assume we wish to shift the letter b by 27 places.假设我们希望将字母 b 移动 27 位。

ASCII value of b which is 98 therefore remainder is b 的 ASCII 值是 98 因此余数是

remainder = 98 + 27 % 122 which is 3余数 = 98 + 27 % 122 即 3

In our second line we simply add the remainder to the array of ascii ints, so our array contains only one single element [98] after the remainder is calculated we simply do.在我们的第二行中,我们简单地将余数添加到 ascii 整数数组中,因此我们的数组仅包含一个元素 [98],在计算余数后我们只需这样做。

96 (lowerbound for lowercase characters) + remainder = 98 therefore our shift from b to 27 places is c. 96(小写字符的下界)+ 余数 = 98 因此我们从 b 到 27 位的移动是 c。

However my code does not work for cases such as A,Z,z and a.但是我的代码不适用于 A、Z、z 和 a 等情况。

I suspect it is to do with my modulo operation.我怀疑这与我的模运算有关。

Can someone please help me out here is my full code and my approach.有人可以帮我吗这是我的完整代码和我的方法。

  1. Convert string to char array将字符串转换为字符数组
  2. Convert char array to ascii int array.将 char 数组转换为 ascii int 数组。
  3. if ascii int value + remainder > 122 for lower case characters calculate remainder and shift to new position else just do value + shift.如果小写字符的 ascii int 值 + 余数 > 122 计算余数并转移到新的 position 否则只做值 + 转移。

My code:我的代码:

public static String caesarCipher(String s, int k) {
    final int UPPER_BOUND_LOWERCASE = 122;
    final int UPPER_BOUND_UPPERCASE = 90;
    int[] asciiArr = new int[s.length()];
    
    //populate ascii Array;
    
    for (int i = 0; i < s.toCharArray().length; ++i) {
        asciiArr[i] = (int) s.toCharArray()[i];
        
        char c = (char) asciiArr[i];
        
        if (asciiArr[i] + k <= UPPER_BOUND_LOWERCASE && Character.isAlphabetic(c) && !Character.isUpperCase(c)) {
            asciiArr[i] = asciiArr[i] + k;
        } else if (asciiArr[i] + k > UPPER_BOUND_LOWERCASE && Character.isAlphabetic(c) && !Character.isUpperCase(c)) {
            int remainder = (asciiArr[i] + k) % UPPER_BOUND_LOWERCASE;
            asciiArr[i] = 96 + remainder;
        }
        if (asciiArr[i] + k <= UPPER_BOUND_UPPERCASE && Character.isAlphabetic(c) && Character.isUpperCase(c)) {
            asciiArr[i] = asciiArr[i] + k;
        } else if (asciiArr[i] + k > UPPER_BOUND_UPPERCASE && Character.isAlphabetic(c) && Character.isUpperCase(c)) {
            int remainder = (asciiArr[i] + k) % UPPER_BOUND_UPPERCASE;
            asciiArr[i] = 64 + remainder;
        }
    }
    return arrayToString(asciiArr);
}


public static String arrayToString(int[] arr) {
    StringBuilder stringBuilder = new StringBuilder();
    
    for (int i = 0; i < arr.length; ++i) {
        stringBuilder.append((char) arr[i]);
    }
    
    return stringBuilder.toString();
}

There's no need in generating a character array via String.toCharArray() ( because it allocates in memory a new array and populates it with the contents of the string ).无需通过String.toCharArray()生成字符数组(因为它在 memory 中分配了一个数组并用字符串的内容填充它)。 Instead, you can iterate over the given String examining each character using method String.charAt() .相反,您可以使用方法String.charAt()遍历给定的String检查每个字符。

The idea of using ASCII codes isn't nice from the perspective of readability of code.从代码可读性的角度来看,使用 ASCII 代码的想法并不好。 And more over, using magic numbers 96 and 64 directly in the code is a huge red flag.而且,直接在代码中使用幻数9664是一个巨大的危险信号。 Avoid doing this, it's very error-prone.避免这样做,它很容易出错。

Conversely, the characters 'A' and 'a' from the perspective of clean-coding are quite self-descriptive, and therefore could be used directly in the code without even introducing a variable.相反,字符'A''a'从clean-coding的角度来看是非常自描述的,因此可以直接在代码中使用,甚至不需要引入变量。

According to the problem description, the value of k is guaranteed to be non-negative.根据问题描述, k的值保证是非负的。 That simplifies the problem - we should deal only with cases when 'Z' turns into 'A' (or subsequent letter after 'A' ), but not the opposite.这简化了问题 - 我们应该只处理'Z'变成'A' (或'A'之后的后续字母)的情况,而不是相反的情况。

To obtain the encrypted character , we need to calculate the difference between the initial character and 'A' (or 'a' ) add k and apply modulo ( % ) 26 (which is the length of English alphabet).要获得加密字符,我们需要计算初始字符与'A' (或'a' )之间的差异加上k并应用模( %26 (这是英文字母表的长度)。 We need to needs to use modulo because k can have a value up to 100 , therefore we need to adjust the difference to the alphabet size.我们需要使用模数,因为k的值可以达到100 ,因此我们需要将差异调整为字母大小。 Then we need to add the adjusted value to 'A' (or 'a' ) and cast the result into char (because the result of the arithmetic operation in Java would be of type int ).然后我们需要将调整后的值添加到'A' (或'a' )并将结果转换为char (因为 Java 中的算术运算结果将是int类型)。

Note: after applying the modulo, the difference between the initial character and base ( 'A' or 'a' ) there's plus k , ie (letter-'A'+k)%26 , this value is guaranteed to be less than 26 .注意:取模后,首字符基数'A''a' )之差有加k ,即(letter-'A'+k)%26 ,保证该值小于26 . Therefore, we can safely add it to the base ( 'A' or 'a' ) without checking if it would be greater than 'Z' or 'z' , like you've done in your code, because it can't be the case.因此,我们可以安全地将它添加到基数'A''a' )而不用检查它是否大于'Z''z' ,就像您在代码中所做的那样,因为它不能案子。

While encrypting a separate character, there are three cases you need to address:在加密一个单独的字符时,您需要解决三种情况:

  • Character is non-alphabetic - simply append it to the result as is.字符是非字母的- 只需 append 就可以得到结果。
  • A lower-case character - encrypted result would be 'a' + (letter - 'a' + k) % 26 .小写字符 - 加密结果为'a' + (letter - 'a' + k) % 26
  • An upper-case character - encrypted result would be 'A' + (letter - 'A' + k) % 26 .大写字符 - 加密结果为'A' + (letter - 'A' + k) % 26

In order to store the result without overheads of string concatenation, we can use StringBuilder .为了在没有字符串连接开销的情况下存储结果,我们可以使用StringBuilder

That's how it might be implemented:这就是它可能的实现方式:

public static final int ALPHA_SIZE = 26;

public static String caesarCipher(String str, int k) {
    
    StringBuilder result = new StringBuilder();
    
    for (int i = 0; i < str.length(); i++) {
        char next = str.charAt(i);
        char encrypted = next;
        
        if (Character.isUpperCase(next)) {
            encrypted = (char) ('A' + (next - 'A' + k) % ALPHA_SIZE);
        }
        if (Character.isLowerCase(next))  {
            encrypted = (char) ('a' + (next - 'a' + k) % ALPHA_SIZE);
        }
        result.append(encrypted);
    }
    return result.toString();
}

This solution passes all test cases on HackerRank .该解决方案通过了HackerRank上的所有测试用例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM