[字符串专项] Manacher算法

Manacher算法

算法过程分析

  • 参考资料 https://subetter.com/algorithm/manacher-algorithm.html
  • 由于回文分为偶回文(比如 bccb)和奇回文(比如 bcacb),而在处理奇偶问题上会比较繁琐,所以这里我们使用一个技巧,具体做法是:在字符串首尾,及各字符间各插入一个字符(前提这个字符未出现在串里)。举个例子:s=“abbahopxpo”,转换为s_new="$#a#b#b#a#h#o#p#x#p#o#"。
  • 定义一个辅助数组int p[],其中p[i]表示以 i 为中心的最长回文的半径
  1. j回文串有一部分在id回文串外
    • p[i] = mx - i;
  2. j回文串均在id回文串内
    • p[i] = p[2*id - 1];
  3. j回文串左端与id回文串左端重合
    • p[i] = p[2*id - 1] / mx - i;
    • while (s_new[i - p[i]] == s_new[i + p[i]]) 
          p[i]++;
      

例题

  • POJ 3974
    • 模版题
      #include <cstdio>
      #include <cstring>
      #include <iostream>
      using namespace std;
      
      typedef unsigned long long ULL;
      const int MAXN = 1000001;
      
      int n, ans = 1, cnt = 0, p[2 * MAXN];
      char s[MAXN], new_s[2 * MAXN];
      
      int getInit()
      {
          int len = strlen(s);
          int cur = 2;
          new_s[0] = '$';
          new_s[1] = '#';
          for (int i = 0; i < len; i++)
          {
              new_s[cur++] = s[i];
              new_s[cur++] = '#';
          }
          new_s[cur] = '\0';
          return cur;
      }
      
      int Manacher()
      {
          int mx = 0, id;
          int len = getInit();
          for (int i = 0; i < len; i++)
          {
              if (i < mx)
                  p[i] = min(p[2 * id - i], mx - i);
              else
                  p[i] = 1;
              while (new_s[i - p[i]] == new_s[i + p[i]])
                  p[i]++;
              if (mx < i + p[i])
              {
                  id = i;
                  mx = i + p[i];
              }
              ans = max(ans, p[i] - 1);
          }
          return ans;
      }
      
  • HDU 4513
    • Manacher + 单调性预处理
      #include <cstdio>
      #include <cstring>
      #include <iostream>
      using namespace std;
      
      typedef unsigned long long ULL;
      const int MAXN = 1000001;
      
      int n, ans = 1, cnt = 0, p[2 * MAXN], t, a[MAXN], h[MAXN];
      int new_a[2 * MAXN];
      
      int getInit()
      {
          int cur = 1;
          new_a[0] = -2;
          for (int i = 1; i <= n; i++)
          {
              new_a[cur++] = a[i];
              new_a[cur++] = -2;
          }
          return cur;
      }
      
      void Manacher()
      {
          int mx = 0, id;
          int len = getInit();
          for (int i = 0; i <= len; i++)
          {
              if (i < mx)
                  p[i] = min(p[2 * id - i], mx - i);
              else
                  p[i] = 1;
              while (new_a[i - p[i]] == new_a[i + p[i]])
                  p[i]++;
              if (mx < i + p[i])
              {
                  id = i;
                  mx = i + p[i];
              }
          }
      }
      
      int main()
      {
          scanf("%d", &t);
          while (t--)
          {
              ans = 0;
              memset(h, 0, sizeof(h));
              scanf("%d", &n);
              for (int i = 1; i <= n; i++)
              {
                  scanf("%d", &a[i]);
                  if (a[i - 1] <= a[i])
                      h[i] = h[i - 1] + 1;
                  else
                      h[i] = 1;
              }
              Manacher();
              for (int i = 0; i <= 2 * n; i++)
              {
                  if (new_a[i] != -2)
                      ans = max(min(h[i / 2] * 2 + 1, p[i] - 1), ans);
                  else
                      ans = max(min(h[i / 2] * 2, p[i] - 1), ans);
              }
              printf("%d\n", ans);
          }
      
          return 0;
      }
      ···
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值