计算java中Caesar密码实现的余数



我正在处理一个HackerBank问题。解决方案这涉及到用k移位将字符串转换为Caesar密码

尤利乌斯·凯撒通过使用密码加密来保护他的机密信息。凯撒的密码将每个字母移动若干个字母。如果移位使你越过字母表的末尾,只需旋转回字母表的前面。在旋转3的情况下,w、x、y和z将映射到z、a、b和c。

Original alphabet:   abcdefghijklmnopqrstuvwxyz 
Alphabet rotated +3: defghijklmnopqrstuvwxyzabc

示例:

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

k=3

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

注意:密码仅对字母进行加密;诸如-之类的符号保持未加密。

功能描述:

string s:明文-不带空格的有效ASCII字符串;int k:字母表旋转因子。

限制:

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

我需要一些帮助来了解我的代码问题的根源。

我的猜测是,它可能位于负责处理大写和小写字母的行中。

小写字母

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

小写字符的上限(最高ascii int值)是z,它等于字符z,它是122。

现在让我们考虑一下我的代码工作的情况。

假设我们想把字母b移27位。

b的ASCII值为98,因此余数为

剩余=98+27%122,即3

在我们的第二行中,我们只需将余数添加到ascii int数组中,因此在计算完余数后,我们的数组只包含一个元素[98]

96(小写字符的小写)+余数=98,因此我们从b到27位的转换是c.

然而,我的代码不适用于A、Z、Z和A等情况。

我怀疑这与我的模运算有关。

有人能帮我吗?这是我的完整代码和方法。

  1. 将字符串转换为char数组
  2. 将char数组转换为ascii int数组
  3. 如果ascii int值+余数>对于小写字符,122计算余数并移位到新位置,否则只执行值+移位

我的代码:

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();
}

无需通过String.toCharArray()生成字符数组(因为它在内存中分配新的数组,并用字符串的内容填充它)。相反,您可以使用方法String.charAt()对给定的String进行迭代,检查每个字符。

从代码可读性的角度来看,使用ASCII代码的想法并不好。此外,在代码中直接使用幻数9664是一个巨大的危险信号。避免这样做,它非常容易出错。

相反,从干净编码的角度来看,字符'A''a'是非常自我描述的,因此可以直接在代码中使用,甚至不引入变量。

根据问题描述,k的值保证为非负。这简化了问题——我们应该只处理'Z'变成'A'(或'A'之后的后续字母)的情况,而不是相反的情况。

为了获得加密字符,我们需要计算初始字符和'A'(或'a')之间的加上k并应用模(%)26(这是英文字母表的长度)。我们需要使用模,因为k的值可以高达100,因此我们需要将调整为字母表大小。然后,我们需要将调整后的值添加到'A'(或'a')中,并将结果强制转换为char(因为Java中算术运算的结果类型为int)。

注意: 应用模后,初始字符之间的差('A''a')加上k,即(letter-'A'+k)%26,该值保证小于26。因此,我们可以安全地将它添加到('A''a'),而无需像您在代码中所做的那样检查它是否大于'Z''z',因为不可能是

在加密单独的字符时,有三种情况需要解决:

  • 字符是非字母-只需按原样将其附加到结果中即可
  • 小写字符加密的结果将是'a' + (letter - 'a' + k) % 26
  • 大写字符加密的结果将是'A' + (letter - 'A' + k) % 26

为了在没有字符串串联开销的情况下存储结果,我们可以使用StringBuilder

这就是它的实现方式:

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();
}

此解决方案通过HackerBank上的所有测试用例。

最新更新