问题分析
你应该首先询问面试官,需要处理的字符串是一个ASCII字符串还是一个Unicode字符串。提出这个问题将展现你对细节的考虑和你坚实的计算机科学基础。为了简单起见,我们将假设字符串为ASCII类型。
一种解决方案是创建一个布尔值数组,其中位于索引i的标志指示字符i是否包含在字符串中。你第二次看到这个标识,你可以立即返回false。如果字符串长度超过了唯一字符的数量,我们也可以立即返回false。
算法
具体的算法实现如下:
public boolean isUniqueChars(String str) {
if (str.length() > 128) {
return false;
}
boolean[] charSet = new boolean[128];
for (int i = 0; i < str.length(); i++) {
int val = str.charAt(i);
if (charSet[val]) { // already found this char in string
return false;
}
charSet[val] = true;
}
return true;
}
这个代码的时间复杂度是O(n),其中n是字符串的长度。
我们可以通过使用位向量来将我们的空间使用减少八倍。我们将假设,在下面的代码中,该字符串只使用小写字母a到z。
/*
* We can reduce our space usage by a factor of eight by using a bit vector.
* We will assume, in the below code, that the string only uses the
* lowercase letters a through z. This will allow us to use just a single
* int.
*/
public boolean isUniqueChars(String str) {
int checker = 0;
for (int i = 0; i < str.length(); i++) {
int val = str.charAt(i) - 'a';
if ((checker & (1 << val)) > 0) {
return false;
}
checker |= (1 << val);
}
return true;
}
进一步的问题
上面的算法,个人认为都是比较典型的以空间换时间的思路。如果我们不能使用额外的数据结构,可以考虑下面的算法实现:
1. 将字符串的每个字符与字符串的每个其他字符进行比较。这将花费O(n2)时间和O(1)空间。
2. 如果允许修改输入字符串,我们可以在O(n log(n))时间内对字符串进行排序,然后再检查相邻字符串是否相同。
算法拓展
接下来,我们来拓展一下本文中使用的算法,来检查两个字符串是否具有相同的字符数(permutation)。我们也可以使用排列的定义 – 具有相同字符数的两个词来实现这个算法。我们只是迭代这个代码,计算每个字符出现的次数。之后,我们比较两个数组。
public boolean permutation(String s, String t) {
if (s.length() != t.length()) {
return false;
}
int[] letters = new int[128];
char[] s_array = s.toCharArray();
for (char c : s_array) {
letters[c]++;
}
for (int i = 0; i < t.length(); i++) {
int c = (int) toString().charAt(i);
letters[c]--;
if (letters[c] < 0) {
return false;
}
}
return true;
}