题目描述:
例如,输入”They are students.”和”aeiou”,则删除之后的第一个字符串变成”Thy r stdnts.”。
思路分析:
总体来说,就是在第一字符中拿到一个字符,判断其是否在第二个字符串中,在的话,就删除该字符。
考虑如下几个问题:
1、如何在字符串中删除一个字符:
字符串的内存是连续分配的,当我们删除其中一个字符时,就需要把后面所有的字符向前移动一个字节的位置。因此,对于一个长度为n的字符串,删除一个字符的时间复杂度为O(n)。对于本题而言,假设第二个字符串的长度为m,有可能要删除的字符个数是m,因此删除的时间复杂度为O(m*n),即O(n^2)。
我们换一种思路。采用在原字符串基础上重新构造删除后的字符串的思路。定义两个指针,一个slow用来构造新的字符串,一个fast用来遍历原字符串。初始两个指针均指向原字符串的第一个位置。如果fast指向的字符不是要删除的字符,就赋值给slow,然后两个指针一起后移;如果是要删除的,就跳过该字符,fast继续遍历下一个字符,slow不变。这样,删除在O(n)时间内就可以搞定。
2、如何在一个字符串中查找一个字符:
我们需要判断第一个字符串中的所有字符是否在第二个字符串中存在。最容易想到的办法是,两层循环遍历,时间复杂度为O(n^2)。
简单方法:采用哈希表的思想:字符串的总数是有限的。对于8位的char型字符,共有2^8=256个字符。新建一个256的数组,把所有元素都初始化为0。对于第二个字符串中每一个字符,把它的ascii码映射为该数组的索引,把数组的该索引对应的值设为1。这样,再查找第一个字符串中的字符是否在第二个字符串中就很方便了,根据字符的ascii码,在数组对应的下标找到其对应的数组值,为0表示第二个字符串中没有该字符,为1,表示有,只需要O(1)时间。这样,查找长度为n的第一个字符串中的字符是否在第二个字符串中,时间复杂度降为O(n)。
代码及测试:
#include <iostream>
#include <assert.h>
using namespace std;
//在一个字符串中删除某些特定字符
char * deleteChars(char *first, char * second) {
assert( (NULL != first) && (NULL != second) );
//标记字符是否在字符串second中出现过
const int tableSize = 256;
bool hashTable[tableSize];
memset(hashTable, 0, sizeof(hashTable));
while( *second != '\0') {
if( !hashTable[ *second ] ) {
hashTable[ *second ] = true;
}
second ++;
}
//删除first中所有出现在second中的字符
char *fast = first;
char *slow = first;
while( *fast != '\0') {
if( !hashTable[*fast]){
*slow = *fast;
++ slow;
}
++ fast;
}
*slow = '\0';
return first;//注意这里应该是first而不是slow,slow此时已经指向first最末尾了
}
void test1() { //second中的字符没有出现在first中的
cout << "*****test1 second中的字符没有出现在first中的:*****";
char text[] = "abcdbbad";
char *first = text;
char second[] = "lmni";
first = deleteChars(first, second);
cout << first << endl;
}
void test2() { //second中的字符有出现在first中的
cout << "*****test1 second中的字符有出现在first中的:*****";
char text[] = "abcdbbad";
char *first = text;
char second[] = "adfghb";
first = deleteChars(first, second);
cout << first << endl;
}
int main() {
test1();
test2();
return 0;
}