Codeforces Global Round 7 - Codeforces1326 D2 Prefix-Suffix Palindrome (Hard version) - Manacher做法

D2. Prefix-Suffix Palindrome (Hard version)

time limit per test :2 seconds                                memory limit per test :256 megabytes

input :standard input                                                 output :standard output

This is the hard version of the problem. The difference is the constraint on the sum of lengths of strings and the number of test cases. You can make hacks only if you solve all versions of this task.

You are given a string s, consisting of lowercase English letters. Find the longest string, t, which satisfies the following conditions:

  • The length of t does not exceed the length of s.
  • tt is a palindrome.
  • There exists two strings a and b (possibly empty), such that t=a+b ( "+" represents concatenation), and a is prefix of s while b is suffix of s.

Input

The input consists of multiple test cases. The first line contains a single integer t (1\leqslant t\leqslant10^{5}), the number of test cases. The next t lines each describe a test case.

Each test case is a non-empty string s, consisting of lowercase English letters.

It is guaranteed that the sum of lengths of strings over all test cases does not exceed 10^{6}.

Output

For each test case, print the longest string which satisfies the conditions described above. If there exists multiple possible solutions, print any of them.

Example

input

5
a
abcdfdcecba
abbaxyzyx
codeforces
acbba

output

a
abcdfdcba
xyzyx
c
abba

Note

In the first test, the string s="a" satisfies all conditions.

In the second test, the string "abcdfdcba" satisfies all conditions, because:

  • Its length is 9, which does not exceed the length of the string ss, which equals 11.
  • It is a palindrome.
  • "abcdfdcba" = "abcdfdc" + "ba", and "abcdfdc" is a prefix of s while "ba" is a suffix of s.

It can be proven that there does not exist a longer string which satisfies the conditions.

In the fourth test, the string "c" is correct, because "c" = "c" + "" and a or b can be empty. The other possible solution for this test is "s".

题目大意

对于给定的字符串s,求由s的某段前缀和某段后缀连接而成的最长的回文串t

思路

1. 先设l = 1,r = len,从字符串s两端进行暴力匹配,知道找到 l 和 r 第一个不匹配的位置(注意循环时l < r,不然会runtime error)。

while (s[l] == s[r] && l < r){
    ++ l;
    -- r;
}

2. 通过步骤1,我们知道1~l - 1与r + 1~len是对称的,显然一个回文串左右两边加上对称的部分依然是个回文串,所以接下来我们要找左端点在l或者右端点在r的最长回文串(两种情况选最长的,而且回文串最小长度为1)。

3. 如果暴力去找最长回文串,我们要枚举中心,然后向两边扩散,复杂度是O\left ( n^{2} \right ),10的六次方显然超时,复杂度应该控制在O\left ( n \right )或者O\left(nlogn \right ),这题有manacher和字符串hash两种写法,这里先讲manacher写法。

4. manacher模板人人都有,不会manacher的可以先看看模板题理解一下 (算法理解可以去看oi - wiki 或者各种算法书和博客)- 51nod 1089 - 最长回文子串 V2(Manacher算法)- Manacher模板题,重点是如何求从端点开始的最长回文串呢?我们知道p[i]表示i所能向两边延伸的最大长度(包括i),比如abcdcba中d所能延伸的最大长度为4,如果从左端开始的最长字符串,从中心点i一定能延伸到左端点,即中心点的下标i - p[i] = 0(我们在使用manacher时填充了特殊字符,所以不是-1),右端点同理。

for (int i = 0; i <= 2 * tot + 1; ++ i){
    if (i - p[i] == 0) len1 = p[i] - 1;
}
for (int i = 2 * tot + 1; i >= 0; -- i){
    if (i + p[i] - 1 == 2 * tot + 1) len2 = p[i] - 1;
}

5. 最后输出即可

for (int i = 1; i < l; ++ i) printf ("%c", s[i]);
if (len1 >= len2){
    for (int i = l; i <= l + len1 - 1; ++ i) printf("%c", s[i]);
}else {
    for (int i = r - len2 + 1; i <= r; ++ i) printf("%c", s[i]);
}
for (int i = r + 1; i <= len; ++ i) printf("%c", s[i]);
printf("\n");

 

代码

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6 + 10;
char s[N];
char t[N], ma[N << 1];
int p[N << 1];
void manacher(char *s, int len){
    int l = 0;
    ma[l ++] = '$';
    ma[l ++] = '#';
    for (int i = 1; i <= len; ++ i){
        ma[l ++] = s[i];
        ma[l ++] = '#';
    }
    ma[l] = 0;
    int  r = 0, c = 0;
    for (int i = 0; i <= l; ++i){
        p[i] = r > i ? min(p[2 * c - i], r - i) : 1;
        while (ma[i - p[i]] == ma[i + p[i]]) ++ p[i];
        if (i + p[i] > r){
            r = i + p[i];
            c = i;
        }
    }
}
int main()
{
    int cas;
    cin >> cas;
    while (cas --){
        scanf("%s", s + 1);
        int len = strlen(s + 1);
 
        int l = 1, r = len;
        while (s[l] == s[r] && l < r){
            ++ l;
            -- r;
        }
        int tot = 0;
        for (int i = l; i <= r; ++ i){
            t[++ tot] = s[i];
        }
        for (int i = 0; i <= 2 * tot + 1; ++ i)p[i] = 0;
        manacher(t, tot);
        int len1 = 0, len2 = 0;
        for (int i = 0; i <= 2 * tot + 1; ++ i){
            if (i - p[i] == 0) len1 = p[i] - 1;
        }
        for (int i = 2 * tot + 1; i >= 0; -- i){
            if (i + p[i] - 1 == 2 * tot + 1) len2 = p[i] - 1;
        }
        for (int i = 1; i < l; ++ i) printf ("%c", s[i]);
        if (len1 >= len2){
            for (int i = l; i <= l + len1 - 1; ++ i) printf("%c", s[i]);
        }else {
            for (int i = r - len2 + 1; i <= r; ++ i) printf("%c", s[i]);
        }
        for (int i = r + 1; i <= len; ++ i) printf("%c", s[i]);
        printf("\n");
    }
    return 0;
}

 

你可能感兴趣的:(#,Manacher)