一道演算法題---把字串內連續相同字元刪除
題目描述:
給定一字串,刪除連續相同的字元,如baiidu刪除後為badu,baiiiau刪除後為bu,baiiabdu刪除後為du。
要做這道題,大家可能第一想法就是找到連續相同字元,然後刪除,把後續字元前移,然後繼續。不斷迭代,直到不再有連續字元為止。這種演算法效率比較低。
仔細觀察發現,其實就是刪除迴文子串問題(但是還有些不一樣,像bab這種迴文串,按題目要求是不能刪除的)。關於迴文子串的演算法,可以看本部落格中的Manacher演算法一章
像baiiabdu可以轉換成
str[] = $ # b # a # i # i # a # b # d # u #
p[] = 0 1 2 1 2 1 2 7 2 1 2 1 2 1 2 1 2 1
下面p陣列代表上面相應字元為中心的迴文子串長度。
我們發現要刪除的子串都是以#為中心的長度大於等於3的迴文子串,或者以字母為中心的長度大於等於4的並且該字母與相鄰的兩個字母相同的迴文子串。那我們就掃描str陣列,把以#為中心的長度為奇數的迴文子串全部置為#。處理後的str為 $ # # # # # # # # # # # # # d # u #
然後輸出從下標1開始的不為#字元的所有字元,即du。
對於baiiiau,奇數連續相同字元,也是能處理的。
str[] = $ # b # a # i # i # i # a # u #
p[] = 0 1 2 1 2 1 2 3 6 3 2 1 2 1 2 1
處理後的str為$ # b # # # # # # # # # # # u #
下面附上程式碼:
/*問題描述:給定一字串,刪除連續相同的字元,如baiidu刪除後為badu,baiiad刪除後為bd*/
#include <iostream>
#include <cstring>
using namespace std;
/*尋找最長迴文子串,Manacher演算法描述http://blog.csdn.net/ggggiqnypgjg/article/details/6645824,
把偶數長度的迴文子串進行刪除*/
char* Manacher(char *src)
{
char *str = new char[2*strlen(src)+3];
char *str2 = new char[2*strlen(src)+3];
str[0] = '$';
str2[0] = '$';
int i = 1;
while(*src != '\0')
{
str[i] = '#';
str2[i] = '#';
i++;
str[i] = *src;
str2[i] = *src;
i++;
src++;
}
str[i] = '#';
str2[i] = '#';
i++;
str[i] = '\0';
str2[i] = '\0';
int len = i;
int mx=0,id;
int p[len];
for(i=1; i<len; ++i)
{
if(mx > i)
{
p[i] = min(p[2*id-i], p[id]+id-i);
}
else
{
p[i] = 1;
}
while(str[i+p[i]] == str[i-p[i]])
{
p[i]++;
}
if(p[i]+i > mx)
{
mx = p[i]+i;
id = i;
}
}
for(i=1; i<len; ++i)
{
if(str[i] == '#') //以#為中心的迴文子串
{
if(p[i] >= 3)
{
for(int j= (i-p[i]+1) ; j<(i+p[i]); ++j)
{
str2[j] = '#';
}
}
}
else //以字母為中心的迴文子串
{
if(p[i]>=4 && str[i]==str[i-2])
{
for(int j= (i-p[i]+1) ; j<(i+p[i]); ++j)
{
str2[j] = '#';
}
}
}
}
char *dest = new char[strlen(src)+1];
char *temp = dest;
for(i=1; i<len; ++i)
{
if(str2[i] != '#')
{
*temp++ = str2[i];
}
}
*temp = '\0';
delete[] str;
delete[] str2;
return dest;}int main(){ cout<<"please input a string:"<<endl; char *s=new char; cin>>s; cout<<Manacher(s)<<endl; return 0;}