zoukankan      html  css  js  c++  java
  • [国家集训队]最长双回文串 manacher

    ~~~题面~~~

    题解:

    首先有一个直观的想法,如果我们可以求出对于位置i的最长后缀回文串和最长前缀回文串,那么我们枚举分界点然后合并前缀和后缀不就可以得到答案了么?

    所以我们的目标就是求出这两个数列,

    我们令f[i] 表示以i为结尾的最长回文后缀的长度,g[i]表示以i为开头的最长回文后缀的长度。

    由于要找回文串,因此我们先想到manacher。

    然后我们可以发现对于这样一个串:

    原串:xxxxxxxxxx

    新串:#x#x#x#x#x#x#x#x#x#x#

    我们可以观察到,假设这时我们已经求出了f和g,那么对于任意位置而言,合并出的最长双回文串中,都是“偶串”,且有一半是'#',一半是原串。

    因此我们可以直接合并f[i-1] 和 g[i],并用ans记录最大值,那么这时真正的答案就是ans / 2.

    假设我们manacher已经求到了第i位,此时i的最长回文半径是R,且j已经被包括在R里面了,那么显然i对j的贡献是(j - i) * 2 + 1。

    而因为贡献只和i与j的位置有关,并且我们的i是从1开始,逐渐递增的,因此对于任意一个j,第一次将它更新的i必然可以产生最优解,因此每个j会且仅会被更新一次。

    而被更新的j显然也是递增的,因为如果j后面的k已经被更新的话,说明之前已经有一个i可以使得k被覆盖在内了。那么j既然在k前面,肯定也被覆盖过了,

    因此求出f[j]时,j肯定是递增的(也就是说肯定是先求出前面的在求出后面的),因此我们可以直接维护一个指针t(这个指针只是字面上的指针),表示当前已经更新到了t,

    一旦被覆盖的MaxRight > t,我们就直接用当前的i来更新t。

    对于前缀回文串,我们直接把字符串翻转过来,然后再做一遍相同的操作,再把得到的数组反转回来就可以了

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int 
     4 #define AC 202000
     5 /*处理出前缀半径和后缀半径,然后合并。可以发现,在扩充了数列之后(#),
     6 获取的最长双回文串一定是偶串,且# 和 真实字符数量相同。
     7 因此ans就是合并后的最长长度/2*/
     8 int n, pos, maxn, ans;
     9 int f[AC], g[AC], tmp[AC], r[AC];
    10 char s[AC], p[AC];
    11 
    12 void pre()//读入
    13 {
    14     scanf("%s", s + 1);
    15     n = strlen(s + 1);
    16     for(R i = n; i; --i) s[i * 2] = s[i];
    17     n = 2 * n + 1;
    18     for(R i = 1; i <= n; i += 2) s[i] = '#';
    19     for(R i = 1; i <= n; i++) p[i] = s[n - i + 1];//把原串倒过来做一遍就是后缀了
    20     s[0] = p[0] = '$', s[n + 1] = p[n + 1] = '$';
    21 }
    22 
    23 void build()//处理出前缀半径和后缀半径
    24 {
    25     int t = 0;
    26     pos = maxn = 0;
    27     for(R i = 1; i <= n; i++)
    28     {
    29         r[i] = maxn > i ? min(r[2 * pos - i], maxn - i + 1) : 1;
    30         while(s[i - r[i]] == s[i + r[i]]) ++r[i];
    31         if(i + r[i] - 1 > maxn)
    32         {
    33             maxn = i + r[i] - 1, pos = i;
    34             if(maxn > t)//更新后缀
    35             {
    36                 for(R j = t + 1; j <= maxn; j++) f[j] = (j - i) * 2 + 1;    
    37                 t = maxn;
    38             }
    39         }
    40     }
    41     pos = maxn = t = 0;
    42     for(R i = 1; i <= n; i++)
    43     {
    44         r[i] = maxn > i ? min(r[2 * pos - i], maxn - i + 1) : 1;
    45         while(p[i - r[i]] == p[i + r[i]]) ++r[i];
    46         if(i + r[i] - 1 > maxn)
    47         {
    48             maxn = i + r[i] - 1, pos = i;
    49             if(maxn > t)//更新后缀
    50             {
    51                 for(R j = t + 1; j <= maxn; j++) tmp[j] = (j - i) * 2 + 1;    
    52                 t = maxn;
    53             }
    54         }
    55     }
    56     for(R i = 1; i <= n; i++) g[i] = tmp[n - i + 1];
    57 //    for(R i = 1; i <= n; i++) printf("%d ", f[i]);
    58 //    printf("
    ");
    59 }
    60 
    61 inline void upmax(int &a, int b)
    62 {
    63     if(b > a) a = b;
    64 }
    65 
    66 void work()//合并
    67 {
    68     for(R i = 1; i <= n; i++)
    69         upmax(ans, f[i - 1] + g[i]);
    70     printf("%d
    ", ans/2);
    71 }
    72 
    73 int main()
    74 {
    75     freopen("in.in", "r", stdin);
    76     pre();
    77     build();
    78     work();
    79     fclose(stdin);
    80     return 0;
    81 }
  • 相关阅读:
    HDU 6096 String (AC自动机)
    [leetcode-771-Jewels and Stones]
    [leetcode-766-Toeplitz Matrix]
    [leetcode-763-Partition Labels]
    [leetcode-762-Prime Number of Set Bits in Binary Representation]
    [leetcode-738-Monotone Increasing Digits]
    [leetcode-760-Find Anagram Mappings]
    [leetcode-744-Find Smallest Letter Greater Than Target]
    [leetcode-753-Open the Lock]
    [leetcode-748-Largest Number At Least Twice of Others]
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9382938.html
Copyright © 2011-2022 走看看