原題
刪除字符串中的“b”和“ac”,需要滿足如下的條件:
-
字符串只能遍歷一次
-
不能夠使用額外的空間
例如:
-
acbac ==> ""
-
aaac ==> aa
-
ababac ==> aa
-
bbbbd ==> d
進一步思考:如何處理aaccac呢,需要做哪些改變呢?
分析
首先要明白從字符串中刪除某些字符該如何實現,顯而易見我們可以把保留的字符拷貝新的字符串中來實現刪除。但是題目要求不能使用額外的空間。那就是將要刪除的字符全部交換到字符串的尾部,然後設置一個'\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’,則拷貝字符
-
狀態轉換非常簡單,就是每次都檢查,是前一個字符爲‘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;