15年國賽 切開字符串

標題:切開字符串

Pear有一個字符串,不過他希望把它切成兩段。
這是一個長度爲N(<=10^5)的字符串。
Pear希望選擇一個位置,把字符串不重複不遺漏地切成兩段,長度分別是t和N-t(這兩段都必須非空)。

Pear用如下方式評估切割的方案:
定義“正迴文子串”爲:長度爲奇數的迴文子串。
設切成的兩段字符串中,前一段中有A個不相同的正迴文子串,後一段中有B個不相同的非正迴文子串,則該方案的得分爲A*B。

注意,後一段中的B表示的是:“...非正迴文...”,而不是: “...正迴文...”。
那麼所有的切割方案中,A*B的最大值是多少呢?

【輸入數據】
輸入第一行一個正整數N(<=10^5)
接下來一行一個字符串,長度爲N。該字符串僅包含小寫英文字母。
【輸出數據】
一行一個正整數,表示所求的A*B的最大值。
【樣例輸入】
10
bbaaabcaba
【樣例輸出】
38
【數據範圍】
對於20%的數據,N<=100
對於40%的數據,N<=1000
對於100%的數據,N<=10^5

題比較難,有一定思維難度,而且代碼實現複雜。首先,我們肯定要求出每個位置爲中心的迴文半徑,這個可以用Manacher算法或Hash。接下來,我們需要求出每個前綴有多少不同的迴文串(這裏提到的所有迴文串都是“正迴文串”即長度爲奇數),然後求出後綴有多少不同的迴文串,和後綴有多少不同的子串。對於迴文串的問題,由Manacher算法的引理可知,不同的迴文串個數是O(N)的,因此我們可以暴力找出所有迴文串,用一個set維護Hash值。接下來,求出這些子串在原串中第一次和最後一次出現的位置,就可以知道前綴和後綴分別有多少不同的迴文串。這一步可以先對原串構建後綴數組,然後在後綴數組中這個子串對應了連續的一段,求區間RMQ即可得到第一次、最後一次出現位置。然後處理“後綴的不同子串”數量。我們知道,如果是求一個完整字符串的不同子串,則構建後綴數組後,總子串數-Σ(相鄰兩項的LCP)就是答案。因此這兒我們考慮從後往前逐個插入後綴,動態維護當前答案——用一個平衡樹或者set維護目前爲止所有後綴的序列,每當插入一個字符串時,只有O(1)個相鄰的LCP被改動,可以通過lower_bound找出前驅後繼然後維護有關操作。整個算法複雜度爲O(Nlog^2N),瓶頸在查找“後綴數組中連續的一段”那邊。對於20%的數據是完全暴力,對於40%的數據也不難設計迴文半徑、不同子串的O(N^2)算法。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <set>
#include <algorithm>
#include <map>
#include <bitset>
#include <vector>
#include <queue>
#include <stack>
#include <utility>
#include <functional>
#include <sstream>
#include <list>
#include <complex>
#include <ctime>

#define maxlongint 2147483647
#define biglongint 2139062143
#define LL long long
#define ULL unsigned long long
#define p_b push_back
#define m_p make_pair
#define l_b lower_bound
#define w1 first
#define w2 second

using namespace std;

typedef pair<int,int> PII;
typedef pair<pair<int,int>,int> PIII;
typedef pair<pair<int,int>,pair<int,int> > PIIII;
typedef pair<double,double> PDD;
typedef pair<double,int> PDI;
typedef pair<string,int> PSI;
typedef pair<pair<double,double>,double> PDDD;
typedef pair<pair<double,double>,pair<double,double> > PDDDD;

const int maxn=100005;
const int base=987654323;

int N,o;
char st[maxn];
int a[maxn],b[maxn];
int mark[maxn],temp[maxn],c[maxn],d[maxn],bb[maxn],c1[maxn],d1[maxn],over[maxn],biao[maxn],t[maxn];
int which[maxn],height[maxn],kuo[maxn],lou2[maxn],rank[maxn],sp[maxn],fp[maxn];
int r1[maxn][18],r2[maxn][18],r3[maxn][18];
PII ls[maxn],tq[maxn*10];
int e,p,u,v,tc,x1,x2,xc,yc,mj,ll,rr,xx,yy;
ULL hash1[maxn],hash2[maxn],powc[maxn],cc;
set<ULL> hwc;
set<ULL>::iterator ltk;
set<int> f;
set<int>::iterator jcb;

bool equal(int a,int b,int c)
{
    ULL k1=hash1[a+c-1]-hash1[a-1]*powc[c];
    ULL k2=hash2[b+c-1]-hash2[b-1]*powc[c];
    if (k1==k2) return true; else return false;
}

bool equal_2(int a,int b,int c)
{
    if ((a+c-1>N)||(b+c-1>N)) return false;
    ULL k1=hash1[a+c-1]-hash1[a-1]*powc[c];
    ULL k2=hash1[b+c-1]-hash1[b-1]*powc[c];
    if (k1==k2) return true; else return false;
}

int getmin_r1(int l,int r)
{
    int cc=lou2[r-l+1];
    return min(r1[l][cc],r1[r-(1<<cc)+1][cc]);
}

int getmin_r2(int l,int r)
{
    int cc=lou2[r-l+1];
    return min(r2[l][cc],r2[r-(1<<cc)+1][cc]);
}

int getmax_r3(int l,int r)
{
    int cc=lou2[r-l+1];
    return max(r3[l][cc],r3[r-(1<<cc)+1][cc]);
}

int main()
{

    scanf("%d",&N);
    scanf("%s",st);
    for (int i=1;i<=N;i++)
    {
        a[i]=st[i-1]-96;
        b[i]=st[N-i]-96;
    }
    // suffix array construction
    for (int i=1;i<=N;i++)
        temp[a[i]]=1;
    for (int i=1;i<=255;i++)
        if (temp[i]==1)
            temp[i]=temp[i-1]+1;
        else
            temp[i]=temp[i-1];
    for (int i=1;i<=N;i++) mark[i]=temp[a[i]];

    for (int i=1;i<=N;i++) c[i]=i;
    for (int i=1;i<=N;i++) ls[i]=m_p(mark[i],c[i]);
    sort(ls+1,ls+N+1);
    for (int i=1;i<=N;i++)
        mark[i]=ls[i].w1,c[i]=ls[i].w2;
    for (int i=1;i<=N;i++) d[c[i]]=i;

    e=1;
    while (e<=N)
    {
        memset(biao,0,sizeof(biao));
        over[N]=N;
        for (int i=N-1;i>=1;i--)
            if (mark[i]!=mark[i+1]) over[i]=i; else over[i]=over[i+1];
        for (int i=N;i>=1;i--)
        {
            p=c[i];
            if (p<=e) continue;
            p=d[p-e];
            u=over[p];
            v=u-biao[u];
            c1[v]=c[i]-e;
            d1[c[i]-e]=v;
            bb[v]=mark[i];
            biao[u]++;
        }
        for (int i=N+1;i<=N+e;i++)
        {
            p=d[i-e];
            u=over[p];
            v=u-biao[u];
            c1[v]=c[p];
            d1[c[p]]=v;
            bb[v]=0;
            biao[u]++;
        }
        for (int i=1;i<=N;i++) c[i]=c1[i],d[i]=d1[i];
        t[0]=0;
        for (int i=1;i<=N;i++)
            if ((mark[i]==mark[i-1])&&(bb[i]==bb[i-1]))
                t[i]=t[i-1];
            else
                t[i]=t[i-1]+1;
        for (int i=1;i<=N;i++) mark[i]=t[i];
        if (mark[N]==N) break;
        e*=2;
    }
    for (int i=1;i<=N;i++) which[mark[d[i]]]=i;
    for (int i=1;i<=N;i++) rank[which[i]]=i;
    /*for (int i=1;i<=N;i++)
    {
        for (int j=which[i];j<=N;j++) cout<<st[j-1];
        cout<<endl;
    }*/
    height[0]=1;
    for (int i=1;i<=N;i++)
    {
        tc=d[i];
        height[tc]=height[d[i-1]]-1;
        if (height[tc]<0) height[tc]=0;
        x1=i,x2=which[d[i]-1];
        while (a[x1+height[tc]]==a[x2+height[tc]]) ++height[tc];
    }
    //for (int i=1;i<=N;i++) cout<<height[i]<<endl;

    //hash array construction
    hash1[0]=0;
    for (int i=1;i<=N;i++) hash1[i]=hash1[i-1]*base+a[i];
    hash2[0]=0;
    for (int i=1;i<=N;i++) hash2[i]=hash2[i-1]*base+b[i];
    powc[0]=1;
    for (int i=1;i<=N;i++) powc[i]=powc[i-1]*base;

    //Manacher-in-hash
    int left,right,mid;
    for (int i=1;i<=N;i++)
    {
        left=0;right=min(i-1,N-i);
        while (left<=right)
        {
            mid=(left+right)/2;
            if (equal(i-mid,N+1-i-mid,mid*2+1)) left=mid+1; else right=mid-1;
        }
        kuo[i]=right;
    }
    //for (int i=1;i<=N;i++) cout<<kuo[i]<<endl;

    //two rmq constructions
    for (int i=N;i>=1;i--)
    {
        r1[i][0]=which[i];
        r2[i][0]=height[i];
        r3[i][0]=which[i];
        for (int j=1;j<=17;j++)
            r1[i][j]=min(r1[i][j-1],r1[i+(1<<(j-1))][j-1]),
            r2[i][j]=min(r2[i][j-1],r2[i+(1<<(j-1))][j-1]),
            r3[i][j]=max(r3[i][j-1],r3[i+(1<<(j-1))][j-1]);
    }
    lou2[1]=0;
    for (int i=2;i<=N;i++) lou2[i]=lou2[i/2]+1;

    o=0;
    for (int i=1;i<=N;i++)
    {
        for (int j=kuo[i];j>=0;j--)
        {
            xc=i-j;
            yc=i+j;
            cc=hash1[yc]-hash1[xc-1]*powc[yc-xc+1];
            ltk=hwc.l_b(cc);
            if ((*ltk)==cc) break;
            ++o,tq[o]=m_p(xc,yc);
            hwc.insert(cc);
        }
    }

    //first part
    memset(fp,0,sizeof(fp));
    memset(sp,0,sizeof(sp));
    for (int i=1;i<=o;i++)
    {
        xc=tq[i].w1,yc=tq[i].w2;
        mj=rank[xc];
        int left=1,right=mj,mid;
        while (left<=right)
        {
            mid=(left+right)/2;
            if (equal_2(which[mid],xc,yc-xc+1)) right=mid-1; else left=mid+1;
        }
        ll=left;
        left=mj,right=N;
        while (left<=right)
        {
            mid=(left+right)/2;
            if (equal_2(which[mid],xc,yc-xc+1)) left=mid+1; else right=mid-1;
        }
        rr=right;
        fp[getmin_r1(ll,rr)+(yc-xc)]++;
        sp[getmax_r3(ll,rr)]++;
    }
    for (int i=1;i<=N;i++) fp[i]=fp[i-1]+fp[i];
    for (int i=N;i>=1;i--) sp[i]=sp[i+1]+sp[i];

    LL ans=0,tdk=0,Q;
    //second part
    for (int i=N;i>=2;i--)
    {
        f.insert(rank[i]);
        jcb=f.l_b(rank[i]);
        if (jcb==f.begin()) xx=-1; else --jcb,xx=(*jcb),++jcb;
        ++jcb;
        if (jcb==f.end()) yy=-1; else yy=(*jcb);
        --jcb;
        if ((xx!=-1)&&(yy!=-1))
            tdk=tdk-getmin_r2(xx+1,yy)+getmin_r2(xx+1,rank[i])+getmin_r2(rank[i]+1,yy);
        else
        {
            if (yy!=-1)
                tdk+=getmin_r2(rank[i]+1,yy);
            else
                tdk+=getmin_r2(xx+1,rank[i]);
        }
        //cout<<tdk<<" "<<rank[i]<<endl;
        Q=(LL)(N-i+1)*(N-i+2)/2;
        ans=max(ans,(LL)fp[i-1]*(Q-tdk-sp[i]));
        //cout<<i<<" "<<fp[i-1]<<" "<<Q-tdk-sp[i]<<endl;
    }
    cout<<ans<<endl;

    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章