题目描述:
给定一字符串,删除连续相同的字符,如baiidu删除后为badu,baiiiau删除后为bu,baiiabdu删除后为du。
要做这道题,大家可能第一想法就是找到连续相同字符,然后删除,把后续字符前移,然后继续。不断迭代,直到不再有连续字符为止。这种算法效率比较低。
仔细观察发现,其实就是删除回文子串问题(但是还有些不一样,像bab这种回文串,按题目要求是不能删除的)。关于回文子串的算法,可以看本博客中的Manacher算法一章
链接如下:http://blog.youkuaiyun.com/gstc110/article/details/8780619
像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.youkuaiyun.com/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;}