题目:删除字符串中的"b"和“ac”,需要满足如下的条件
1 字符串只能遍历一次
2 不能够使用额外的空间
例如:
1 acbac ===>" "
2 aaac ===>"aa"
3 ababac ===>"aa"
4 bbbbd ===>"d"
我的解答程序如下:
<span style="font-size:18px;"><pre name="code" class="cpp"><span style="font-size:18px;">#include <stdio.h>
#include <stdlib.h>
void delete_char(char *str)
{
int i=0,j=0; //i表示不会删除字符的位置
for(i=0;str[i]!='\0';i++)
{
if(str[i]!='a'&&str[i]!='b')
{
str[j] = str[i];
j++;
}
else if(str[i]=='a'&&str[i+1]!='c')
{
str[j] = str[i];
j++;
}
else
{
if(str[i]=='a')
{
str[i]='\0';
i++;
}
str[i]='\0';
}
}
str[j]='\0';
}
int main()
{
char st[20]="\0";
printf("enter char:");
gets(st);
delete_char(st);
printf("output:%s",st);
return 0;
}</span>
</span>
我试过了,结果能够输出。
经过分析,我发现这段程序效率不高。 看到了别人的分析,我立刻清晰了起来,在这里贴上:转载,非原创,但很具有参考性。
题目解析:
首先要明白从字符串中删除某些字符该如何实现,显而易见我们可以把保留的字符拷贝新的字符串中来实现删除。但是题目要求不能使用额外的空间。那就是将要删除的字符全部交换到字符串的尾部,然后设置一个'\0'表示字符串的结尾。
其次,如果要删除的都是单个字符的字符串,就很直接:我们使用i和j两个变量遍历字符串,i表示不会删除的字符的位置,j从0开始,只要i所在位置的字符不是要删除的字符,就str[j]=str[i](str表示字符串),然后j++指向下一个位置。一次遍历即可,不需要额外申请空间,只需要两个变量。
但是,现在删除的字符串中有多个字符的,如:“ac”。那要如何处理呢?这里介绍一个小技巧:状态机。这里,我们有两个状态:ONE和TWO。TWO表示,前一个字符时‘a’的状态,其他的都用ONE表示。还是采用前面所描述的遍历方法:
-
如果当前状态为ONE,则拷贝:str[j]=str[i];但如果当前字符满足以下两种状态的任一个,则不进行拷贝:
-
当前字符是‘a’,我们要考虑下一个字符是c
-
当前字符是‘b’,因为我们要删除b
-
-
如果当前状态为TWO:
-
当前字符不是‘c’,那么我们要先拷贝前一个字符‘a’
-
然后考虑当前字符,如果不是‘b’或者‘a’,则拷贝字符
-
代码如下:
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">#include <stdio.h>
#include <stdlib.h>
#define ONE 1
#define TWO 2
void stringFilter(char *str)
{
int state = ONE; //初始状态为one,前一个字符不是'a'
int j = 0;
int i=0;
for(i=0;str[i]!='\0';i++)
{
if(state==ONE&&str[i]!='a'&&str[i]!='b')
{
str[j]=str[i];
j++;
}
//当前状态为TWO
if(state==TWO&&str[i]!='c')
{
str[j]='a';
j++;
if(str[i]!='a'&&str[i]!='b')
{
str[j]=str[i];
j++;
}
}
state = (str[i] == 'a')? TWO:ONE;//状态转换
}
if (state==TWO) //要考虑最后字符为a的情况
{
str[j] = 'a';
j++;
}
str[j]='\0';
}
int main()
{
char st[20]="\0";
printf("enter char:");
gets(st);
stringFilter(st);
printf("output:%s",st);
return 0;
}</span></span></span>
输出结果大家自己编译就好了, 本文的状态机的思想是我们需要学的的知识点。