删除字符

来源:互联网 发布:淘宝网上兼职可靠吗 编辑:程序博客网 时间:2024/06/05 16:33

原题

删除字符串中的“b”和“ac”,需要满足如下的条件:

  1. 字符串只能遍历一次

  2. 不能够使用额外的空间

例如:

  1. acbac ==> ""

  2. aaac ==> aa

  3. ababac ==> aa

  4. bbbbd ==> d

进一步思考:如何处理aaccac呢,需要做哪些改变呢?

分析

首先要明白从字符串中删除某些字符该如何实现,显而易见我们可以把保留的字符拷贝新的字符串中来实现删除。但是题目要求不能使用额外的空间。那就是将要删除的字符全部交换到字符串的尾部,然后设置一个'\0'表示字符串的结尾。

其次,如果要删除的都是单个字符的字符串,就很直接:我们使用i和j两个变量遍历字符串,i表示不会删除的字符的位置,j从0开始,只要i所在位置的字符不是要删除的字符,就str[j]=str[i](str表示字符串),然后j++指向下一个位置。一次遍历即可,不需要额外申请空间,只需要两个变量。

但是,现在删除的字符串中有多个字符的,如:“ac”。那要如何处理呢?这里介绍一个小技巧:状态机。这里,我们有两个状态:ONE和TWO。TWO表示,前一个字符时‘a’的状态,其他的都用ONE表示。还是采用前面所描述的遍历方法:

  1. 如果当前状态为ONE,则拷贝:str[j]=str[i];但如果当前字符满足以下两种状态的任一个,则不进行拷贝:

    • 当前字符是‘a’,我们要考虑下一个字符是c

    • 当前字符是‘b’,因为我们要删除b

  2. 如果当前状态为TWO:

    • 当前字符不是‘c’,那么我们要先拷贝前一个字符‘a’

    • 然后考虑当前字符,如果不是‘b’或者‘a’,则拷贝字符

状态转换非常简单,就是每次都检查,是前一个字符为‘a’。基本代码如下:

void stringFilter(char* str){bool state=false;//前一个不是‘a’unsigned int j=0;for(int i=0;str[i]!='\0';++i){if(!state&&str[i]!='a'&&str[i]!='b'){str[j]=str[i];j++;}if(state&&str[i]!='c'){str[j]='a';j++;if(str[i]!='a'&&str[i]!='b'){str[j]=str[i];j++;}}state=(str[i]=='a')?true:false;}if(state){str[j]='a';j++;}str[j]='\0';}

调用:

<span style="color:#3e3e3e;">int _tmain(int argc, _TCHAR* argv[]){char str[]="ababac";//这里一定不能声明为char* str="ababac",会出现</span><span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px;">写入位置时发生冲突的错误</span><span style="color:#3e3e3e;">stringFilter(str);cout<<str;system("pause");return 0;}</span>
上面不能那样声明的原因是:char* str="ababac";str指向的是静态存储区,"ABCD"是位于常量区的,那么这些值就不能被修改。指针str只是指向了这个位置(指针str存在栈中)。而char str[]="ababac"; 这个数组的存储空间是在栈中开辟的,就是在栈中开辟了整个数组,用来存储数组元素,所以,可以被修改。

下面进一步考虑: 根据上面的算法,我们考虑aaccac,最终得到ac。ac在题目中要求的也是要删除的。是否要删除这个ac,就需要和面试官进行交流了,无论如何,总是要考虑这种情况。还是采用上面的算法,怎么解决删除之后还可以删除的情况?其实非常简单,只需要做很小的修改,我们在循环最后加上这个代码即可:

if(j>1&&str[j-2]=='a'&&str[j-1]=='c')        j=j-2;

原文链接:http://mp.weixin.qq.com/s?__biz=MjM5ODIzNDQ3Mw==&mid=200437914&idx=1&sn=c69d73c76291562856f8221ecc491ac4#rd


0 0