这是一个仅使用原始 Java 功能的解决方案,它使用布尔数组来标记已经“命中”的字符。它依赖于ASCII Table 的顺序,以及只有大写字符的字符串:
private static void printRepeats(String test) {
// "only upper-case characters" requires options 'A' to 'Z'.
// this is asciiValue('Z') - asciiValue('A') (implicitly done by Java) + 1
boolean[] charHits = new boolean['Z' - 'A' + 1];
for (int i=0; i < test.length(); i++){
// our current character
char current = test.charAt(i);
if (charHits[current - 'A']){
// character index already set in charHits array - already seen!
// print and return
System.out.println(
current + " is repeated, first occurrence "
+ test.indexOf(current) + ", second: " + i
);
return;
} else {
// first hit - mark as already occurred for future iterations
charHits[current - 'A'] = true;
}
}
// no character repeats found
System.out.println("No repeats");
}
这个想法是迭代字符串,每次我们看到一个字符,检查我们是否已经看到它。如果我们这样做了,我们会立即打印并返回。如果我们没有 - 我们将其标记为已经“命中”并继续。
代码演练 (printRepeats):
第 1 部分 - 数组分配:
为了存储我们的“命中”,我们需要一个可以查找过去字符的数据结构。第一条语句创建了一个我们需要的大小的数组。由于大写字母在 ASCII 表中是连续的(A - 65 到 Z - 90),我们知道我们只需要一个 [90 - 65 + 1 = 26] 大小的数组来容纳所有大写字母。每个字符命中都映射到数组中的索引[the characters ASCII value] - [A's ASCII value]。所以:
LETTER -> INDEX IN ARRAY
'A' -> 0
'B' -> 1
...
'Z' -> 25
所以要分配这样的数组,我们使用:
boolean[] charHits = new boolean['Z' - 'A' + 1];
这正是像:
new boolean[26];
您可以通过以下方式检查:
System.out.println('Z' - 'A' + 1); // 26
甚至:
System.out.println(new boolean['Z' - 'A' + 1].length); // 26
第 2 部分 - 字符串迭代:
这部分迭代给定的String,有两个选项:
- 将字符标记为
seen
-
发现重复,打印并返回
- 知道布尔数组会自动初始化为
false 很有用,因此我们可以依赖它。
循环代码:
for (int i=0; i < test.length(); i++){
// our current character
char current = test.charAt(i);
if (charHits[current - 'A']){
// character index already set in charHits array - already seen!
// print and return
System.out.println(
current + " is repeated, first occurrence "
+ test.indexOf(current) + ", second repeat: " + i
);
return;
} else {
// first hit - mark as already occurred for future iterations
charHits[current - 'A'] = true;
}
}
// no character repeats found - no index in array hit twice
System.out.println("No repeats");
}
打印部分可以通过使用一些巧妙的映射到原始对象而不是使用String.indexOf,将第一次出现的索引与其“已看到/未看到”状态一起存储,从而进一步优化,但它会降低代码的可读性。
提示:我不会费心记住特定的 ASCII 表 值(任何人也不应该)。我只记住 ASCII 表的 order(这种情况 - 大写英文字母是连续的)。
奖金 - 一个简短而浪费的版本:
一个较短的版本可以组成如下:
对于每个可能的前缀(短到长),检查前缀后面的第一个字符是否在前缀中。如果是,那就是第一次重复。
代码:
public static void printRepeatsShort(String test){
for (int i=0; i< test.length(); i++){
// check index of following character in prefix
int indexInPrefix = test.substring(0, i).indexOf(test.charAt(i));
if ((indexInPrefix != -1){
// next character is in prefix, return
System.out.println(
"Repeated char: '" + test.charAt(i) + "', First: "
+ indexInPrefix + " Second: " + i
);
return;
}
}
System.out.println("No repeats found");
}
这个版本远没有那么理想,但只依赖于来自String 的方法,所以它包含更多内容。