马拉车算法思想

参考链接:传送门1  and 传送门2

马拉车算法是用来查找一个字符串的最长回文串的线性方法。正读与反读都一样的字符串称为回文字符串。比如 “google” 的最长回文子串为 “goog”。这个算法的总框架是,遍历所有的中心点,寻找每个中心点对应的最长回文子串,然后找到所有中心点对应的最长回文子串。
由于回文串的长度可以是奇数也可以是偶数。比如:non,与noon。故马拉车算法的第一步是对字符串做预处理:在每一个字符的左右都加上一个特殊的字符,如  #   则  non ——>#n#o#n#     noon——>#n#o#o#n#   经过了预处理,不论原来的字符串是奇数个还是偶数个,现在都是奇数个。
接下来需要一个 与处理后的字符串t等长 的数组p , p[i]记录 以t[i]为中心的回文串的半径(不包括p[i]本身)
若p[i]=0表示,该回文子串就是t[i]自身。比如 “#a#b#” 的半径数组为 [0, 1, 0, 1  0]。
为了避免在搜索回文子串时,总是判断数组是否越界,我们可以在字符串 t 的首尾各加上一个标志字符。(注意这两个字符应在字符串 t 中没有出现过)。如:#n#o#n# ——>  ^#n#o#n# $   数组 p 的最大半径,就是我们要寻找的最长回文子串的半径,只要把 p 数组求出来,我们便可以找到答案了。
如何计算p 数组: 
马拉车算法在计算 p 数组时,一直在更新两个变量:
id : 回文子串的中心位置     mx: 回文子串的末端位置
使用这两个便可以用一次扫描来计算出整个p数组。
公式:  p[i] = min( p[2*id - i] , mx - i);  

 
        

 

 

 

当 mx -  i > p[j]时,以 s[j] 为中心的回文子串  必定包含在  以s[id]为中心的回文子串中, 由于 i 和 j 对称 ,以 s[i]为中心的回文子串 也一定包含在 以s[id]为中心的回文子串中,所以p[i]=p[j],  由于 j 到id之间的距离 等于  id到 i 的距离。 既  id - j = i - id 移项可得  j = 2*id - i 

当 mx - i <=p[j]时, 以 s[j] 为中心的回文子串不一定完全包含于以 s[id]为中心的回文子串中,据对称性可知,下图中两个绿框所包围的部分相同,意思就是,以s[i]为中心的回文子串,向右可以扩张到mx位置,即 p[i] = mx - i;  对于mx之后的部分是否对称,可以用while循环去判断。

       

代码:

void init()
{
    int k=0;
    str[k++] = '$';//字符串起始位置的特殊标志//字符串末不需要再加标志了  '\n'就可以代替了     //防溢出
    for(int i=0;i<len;i++)
    {
        str[k++]='#';
        str[k++]=s[i];
    }
    str[k++]='#';//最后一个字母后的符号
    len=k;//字符串更新后的长度为k
    
}

int Manacher()
{
  p[0] = 0;
  int sum = 0;//记录最长回文串
  mx = 0;
  for(int i=1;i<len;i++)
  {
    if(i < mx) p[i] = min(mx - i, p[2 * id - i]);
    else p[i] = 1;
    /*p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;*/
    while(str[i - p[i]]== str[i + p[i]]) //向两边扩展更新p[i]  //因为预处理的设置,这里不必担心溢出问题
        p[i]++;
    if(p[i] + i > mx)//回文半径变大
    {
      mx = p[i] + i;//更新回文串能达到的末端位置
      id = i;//更新回文串中心点
      sum = max(sum, p[i]);//更新回文串的最长长度
    }
  }
  return (sum - 1);
}




 

 

 

          

 


 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值