算法面试主题的视频内容请参看:
如何进入Google,算法面试之道
字符串的处理在编程中,几乎无处不在,从脚本处理,前端开发,到生物信息算法,无不广泛而又深入的涉及到字符串的处理。在面试算法中,各种有关字符串处理的算法题,经常用来检测候选人的编程功底,从本节开始,我们逐步展示字符串的相关处理算法,先从简单开始,对于复杂的字符串算法,往往涉及到哈希表和动态规划,相关内容,在后面我们会逐步涉及到。
我们先看两道看似简单,但编码起来容易出错的字符串算法题。
游程编码
游程编码(Run-length encoding RLE) 是一种在线高效的字符串压缩算法,它的思路很简单,将连续出现的字符转换成它重复出现的次数和该字符的组合,例如字符串”aaaabcccaa” 编码后为”4a1b3c2a”, 对于编码后的字符串,例如“3e4f2e” ,解码后为”eeeffffee”, 要求编写编码算法,假定要编码的字符串只包含26个字母,没有数字,同时编写解码算法代码,假定要解码的字符串是合法编码过的。
这道题目的算法实现不难,但要想写出准确的代码实现,也不容易。我们先看编码,编码算法就是遍历整个字符串,统计每个字符连续出现的次数,然后将连续出现的字符转换为次数和字符的组合,代码如下:
public String encode(String s) {
String str = "";
char last = s.charAt(0);
int count = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == last) {
count++;
} else {
str += count;
str += last;
count = 1;
last = s.charAt(i);
}
}
str += count;
str += last;
return str;
}
上面的编码实现不难,但为什么说使得代码准确不容易呢,主要在于,大多数人在白板上写代码时,很容易忘记最后两句:
str += count;
str += last;
漏了这两句的话,字符串”aaaabcccaa” 运行后的结果是”4a1b3c”,这种边界条件的检测和处理是检验候选人编码功底是否扎实和心态是否严谨的重要手段,简单的题目,95%的人都能写出,但也只有5%的人能够注意边界条件,写出完全正确的代码,所以大家在平日积累和现场写代码时,一定要小心边界问题的处理。
最后,解码算法也简单,每次读入两个字符,第一个字符是出现次数,第二个字符是对应重复出现的字符,代码如下:
public String decode(String s) {
String str = "";
for (int i = 0; i < s.length(); i += 2) {
int count = Character.digit(s.charAt(i), 10);
char c = s.charAt(i+1);
for (int j = 0; j < count; j++) {
str += c;
}
}
return str
字符串中的单词逆转
给定一个字符串,它由若干个单词组成,每个单词间以空格分开。我们想对字符串进行转换,使得字符串中单词出现的顺序发生逆转,例如字符串 “Alice like Bob”, 转换后变为 “Bob like Alice”, 要求编写算法实现该转换,同时算法的空间复杂度必须为O(1).
我们先看看,字符串反转是怎么做的,例如将字符串“abcd”反转后,结果为”dcba”, 字符串反转通常做法是用两个指针,分别指向头字符和尾字符,将指针指向的两个字符交换,然后头指针向后移动一个位置,尾字符向前挪动一个位置,当两个指针位置互换时,反转结束,例如:
->a b c d<-
前后指针字符交换后为:
d -> b c<- a
再次交换:
d c<- -> b a
由于两个指针位置互换,倒转结束。
单词倒转可以转换为字符倒转,首先将整个字符串倒转,例如”Alice like Bob” 字符倒转后为 “boB ekil ecilA”, 然后将每一个单词进行字符倒转便有:
“Bob like Alice”
这样我们的目的就达到了,由于字符倒转不需要分配新的空间,因此整个算法空间复杂度为O(1).
算法实现如下:
public class WordReverse {
private StringBuffer inputStr;
public WordReverse(StringBuffer s) {
inputStr = s;
}
public StringBuffer reverseWord() {
reverseByChar(0, inputStr.length() - 1);
int begin = 0, end = begin;
do {
while (end < inputStr.length() && inputStr.charAt(end) != ' ' ) {
end++;
}
//反转单词
reverseByChar(begin, end - 1);
begin = end + 1;
end = begin;
} while (end < inputStr.length());
return inputStr;
}
private void reverseByChar(int begin, int end) {
if (begin < 0 || end >= inputStr.length()) {
return;
}
while (begin < end) {
char c = inputStr.charAt(begin);
char e = inputStr.charAt(end);
inputStr.setCharAt(begin, e);
inputStr.setCharAt(end, c);
begin++;
end--;
}
}
}
reverseByChar 将整个字符串进行基于字符的反转,如果输入字符串为:”Alice like Bob” 执行该函数后,字符串为:”boB ekil ecilA”。
reverseWord 在倒转后的字符串基础上,通过空格查找到每个单词的起始和结尾,然后对每个单词进行反转,执行该函数后,字符串转换为:
“Bob like Alice”
这样,我们的目的就实现了。