Codeforces-1913F:Palindromic Problem(后缀数组)

F. Palindromic Problem
time limit per test:5 seconds
memory limit per test:512 megabytes
input:standard input
output:standard output

You are given a string

s

s

s of length

n

n

n, consisting of lowercase Latin letters.

You are allowed to replace at most one character in the string with an arbitrary lowercase Latin letter.

Print the lexicographically minimal string that can be obtained from the original string and contains the maximum number of palindromes as substrings. Note that if a palindrome appears more than once as a substring, it is counted the same number of times it appears.

The string

a

a

a is lexicographically smaller than the string

b

b

b if and only if one of the following holds:

  • a

    a

    a is a prefix of

    b

    b

    b, but

    a

    b

    a
    e b

    a=b;

  • in the first position where

    a

    a

    a and

    b

    b

    b are different, the string

    a

    a

    a contains a letter that appears earlier in the alphabet than the corresponding letter in

    b

    b

    b.

Input
The first line contains one integer

n

 

(

1

n

3

?

1

0

5

)

n (1le nle 3?10^5)

n (1≤n≤3?105) — the number of characters in the string.

The second line contains the string

s

s

s itself, consisting of exactly

n

n

n lowercase Latin letters.

Output
In the first line, print one integer — the maximum number of palindromic substrings that can be obtained using the operation described in the statement at most once.

In the second line, print the string that can be obtained from

s

s

s and has the maximum possible number of palindromic substrings. If there are multiple answers, print the lexicographically smallest one.

Examples
input
5
aabaa
output
15
aaaaa

input
5
aaaaa
output
15
aaaaa

input
4
awoo
output
7
aooo

思路:考虑遍历

S

S

S,枚举求出替换每个字符后的最多回文串数量。记

L

x

L_x

Lx?为以

x

x

x为中心的回文串半径,当我们枚举替换到第

k

k

k个位置的字符时,回文串数量有以下2种情况会减少:

  • 减少

    (

    x

    +

    L

    x

    ?

    k

    )

    (x+L_x-k)

    (x+Lx??k),其中

    x

    <

    i

    x<i

    x<i 且

    x

    +

    L

    x

    >

    k

    x+L_xgt k

    x+Lx?>k,即在回文串中心

    x

    x

    x在

    k

    k

    k前面,并且该回文串包含

    k

    k

    k,当替换

    S

    k

    S_k

    Sk?时,这个回文串长度会变短。

  • 减少

    (

    k

    ?

    (

    x

    ?

    L

    x

    )

    )

    (k-(x-L_x))

    (k?(x?Lx?)),其中

    x

    >

    i

    x>i

    x>i 且

    x

    ?

    L

    x

    <

    k

    x-L_xlt k

    x?Lx?<k,即在回文串中心

    x

    x

    x在

    k

    k

    k后面,并且该回文串包含

    k

    k

    k,当替换

    S

    k

    S_k

    Sk?时,这个回文串长度会变短。

回文串减少的数量,我们可以用manacher算法或者后缀数组预处理求出每个

L

i

L_i

Li?,根据上面条件,利用前缀和求出替换每个

S

k

S_k

Sk?后,回文串会减少的数量

d

k

d_k

dk?。
接下来只需要求出替换

S

k

S_k

Sk?后可能会增加的回文串数量,容易想到当且仅当

x

+

L

x

=

k

 

(

x

<

k

)

x+L_x=k (x<k)

x+Lx?=k (x<k)或者

x

?

L

x

=

k

 

(

x

>

k

)

x-L_x=k (x>k)

x?Lx?=k (x>k)时,即回文串

L

x

L_x

Lx?长度刚好在

k

k

k处终止,此时替换

S

k

S_k

Sk?才可能会使

L

x

L_x

Lx?变长、回文串数量增加。

L

x

L_x

Lx?增加的长度即增加的回文串数量,而增加的长度就是

S

[

x

?

L

x

,

.

.

.

,

0

]

S[x-L_x,...,0]

S[x?Lx?,...,0]和

S

[

k

,

.

.

.

,

n

]

 

(

x

<

k

)

S[k,...,n] (x<k)

S[k,...,n] (x<k)的最长公共前缀

(

L

C

P

)

(LCP)

(LCP)的长度,同理当

x

>

k

x>k

x>k时,增加的长度为

L

C

P

(

S

[

k

,

.

.

.

,

0

]

,

S

[

x

+

L

x

,

.

.

.

,

n

]

)

LCP(S[k,...,0],S[x+L_x,...,n])

LCP(S[k,...,0],S[x+Lx?,...,n])的长度。

求公共前缀,我们就可以将

S

S

S翻转后与原串

S

S

S拼接,然后利用后缀数组

log

?

2

n

log_2n

log2?n 查询

L

C

P

LCP

LCP的长度。
求出回文串的减少和增加数量后,直接更新答案即可。复杂度

O

(

26

?

n

?

log

?

2

n

)

O(26?n?log_2n)

O(26?n?log2?n)。

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=3e5+10;
const int MOD=998244353; //G=3
const int INF=INT_MAX/2;
const double PI=acos(-1.0);
typedef long long ll;
struct SA
{
    int n;
    vector<int>sa,rk,height,c;
    vector<vector<int>>nex;
    SA(const string &s)
    {
        n=sz(s);
        rk.resize(n);height.resize(n);
        sa.resize(n);nex.resize(n);

        int m=*max_element(s.begin(),s.end());
        c.assign(m+1,0);
        for(int i=0;i<n;i++)c[rk[i]=s[i]]++;
        for(int i=1;i<=m;i++)c[i]+=c[i-1];
        for(int i=0;i<n;i++)sa[--c▼显示]]=i;

        for(int w=1;;w<<=1)
        {
            m=RadixSort(m,w);
            if(m+1>=n)break;
        }
//        for(int i=0;i<n;i++)printf("%d ",rk[i]);puts("");
        for(int i=0,k=0;i<n;i++)
        {
            if(rk[i]==0)continue;
            if(k)k--;
            while(i+k<n&&sa[rk[i]-1]+k<n&&s[i+k]==s[sa[rk[i]-1]+k])k++;
            height[rk[i]]=k;
        }
//        for(int i=0;i<n;i++)printf("%d ",height[i]);puts("");
        for(int i=n-1;i>=0;i--)
        {
            nex[i].assign(31,n);
            nex[i][0]=height[i];
            for(int j=1,w=2;i+w-1<n;j++,w<<=1)nex[i][j]=min(nex[i][j-1],nex[i+w/2][j-1]);
        }
    }
    int RadixSort(int m,int w) {
        c.assign(m+1,0);
        static vector<int>tmp; tmp.resize(n);
        static vector<int>oldrk; oldrk=rk;

        int p=0;
        for(int i=n-1;i>=max(0,n-w);i--)tmp[p++]=i;
        for(int i=0;i<n;i++)if(sa[i]>=w)tmp[p++]=sa[i]-w;

        for(int i=0;i<n;i++)c[rk[i]]++;
        for(int i=1;i<=m;i++)c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--)sa[--c[rk[tmp[i]]]]=tmp[i];

        auto neq=[&](int i,int j,int w){
            if(oldrk[i]!=oldrk[j])return true;
            int rki=i+w<n?oldrk[i+w]:-1;
            int rkj=j+w<n?oldrk[j+w]:-1;
            return rki!=rkj;
        };
        p=0;
        rk[sa[0]]=0;
        for(int i=1;i<n;i++)rk[sa[i]]=neq(sa[i],sa[i-1],w)?++p:p;
        return p;
    }
    int LCP(int x,int y)
    {
        int ret=n-x;
        x=rk[x],y=rk[y];
        if(x>y)swap(x,y);
        x++;
        for(int j=30;j>=0;j--)
        {
            if(x+(1<<j)-1>y)continue;
            ret=min(ret,nex[x][j]);
            x+=1<<j;
        }
        return ret;
    }
};
ll d[MAX],len[MAX];
vector<int>e[MAX];
int solve()
{
    int n;
    string s;
    cin>>n>>s;
    string rs=s;
    reverse(rs.begin(),rs.end());

    SA sa(s+"$"+rs+"@");
    ll tot=0;
    for(int i=0;i<n;i++)
    {
        int even=sa.LCP(i,2*n-i+1);
        d[i-even]+=1;
        d[i]-=1;
        d[i+1]-=1;
        d[i+even+1]+=1;
        if(i-even-1>=0&&i+even<n)
        {
            e[i-even-1].push_back(i+even);
            e[i+even].push_back(i-even-1);
        }

        int odd=sa.LCP(i,2*n-i);
        d[i-odd+1]+=1;
        d[i+1]-=2;
        d[i+odd+1]+=1;
        len[i]=odd;
        tot+=even+odd;
        if(i-odd>=0&&i+odd<n)
        {
            e[i-odd].push_back(i+odd);
            e[i+odd].push_back(i-odd);
        }
    }
    for(int i=1;i<n;i++)d[i]+=d[i-1];
    for(int i=1;i<n;i++)d[i]+=d[i-1];
    ll ans=tot,idx=0,gt=0;
    for(int i=0;i<n;i++)
    {
        for(char c='a';c<='z';c++)
        {
            ll cur=tot-d[i]+len[i];
            if(c==s[i])continue;
            for(int j:e[i])
            {
                if(s[j]!=c)continue;
                if(j>i)cur+=1+sa.LCP(j+1,2*n-i+1);
                else cur+=1+sa.LCP(i+1,2*n-j+1);
            }
            if(cur<ans)continue;
            if(cur>ans){ans=cur,idx=i,gt=c-s[i];continue;}
            if(idx==i&&gt<c-s[i])continue;
            if(idx!=i&&(gt<0||(gt==0&&c>=s[i])))continue;
            ans=cur,idx=i,gt=c-s[i];
        }
    }
    s[idx]+=gt;
    cout<<ans<<endl;
    cout<<s<<endl;
    return 0;
}
int main()
{
    int T=1;
//    cin>>T;
    while(T--)solve();
    return 0;
}