1. 程式人生 > 其它 >21.6.1 t1

21.6.1 t1

6.1還在考試的屑

tag:SAM,PAM


最終的迴文串一定由這樣的形式組成:

  • \(s1+s2+s3\)

其中 \(s1\)\(a\) 串子串,\(s3\)\(b\) 串子串,且 \(s1=rev(s3)\)

\(s2\) 為一個迴文串(可以是 \(a\) 的子串也可以是 \(b\) 的子串)


\(s2\) 可以看作是求以一個位置為左/右端點的最長迴文字串,可以 \(PAM\) 解決 \(O(n)\)

\(s1,s3\) 可以用SAM求出。往廣義SAM裡分別插入 \(a\)\(rev(b)\),列舉 \(s1\) 的右端點 \(i\),然後答案就是 \(\max(len(lca(posa_i,posb_j)))+(\)

\(i+1\)為左端點的最長迴文子串\()\)

然後你會發現那個 \(\max\) 一定是取dfs序上最接近 \(posa_i\) 的兩個點。

(很顯然這是個十分繁瑣的做法)


所以理論複雜度是 \(O(n)\)(這裡的lca可以離線),但是由於常數過大導致 \(8e5\) 本地跑了 \(3.5s\),得分 \(70\)。(其中建SAM用時\(1.5s\),建PAM用時\(0.06s\)

不知道是否有更優秀的做法。


update:標算是對一個串建SAM然後用另一個串跑匹配,應該是常數更優秀


根據標算改的程式碼,最大點247ms

#include<bits/stdc++.h>
using namespace std;

template<typename T>
inline void Read(T &n){
	char ch; bool flag=false;
	while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
	for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
	if(flag)n=-n;
}

enum{
    MAXN = 800005
};

namespace PAM{
    char s[MAXN];

    struct node{
        int son[26], fa, len;
        #define son(x,opt) t[x].son[opt]
        #define fa(x) t[x].fa
        #define len(x) t[x].len
    }t[MAXN];
    int node_cnt;

    inline int Find(int x, int r){
        while(s[r-len(x)-1]!=s[r]) x = fa(x);
        return x;
    }

    int prv;
    inline int insert(int r){
        prv = Find(prv,r); int now = son(prv,s[r]-'a');
        if(!now){
            now = ++node_cnt;
            len(now) = len(prv)+2;
            fa(now) = son(Find(fa(prv),r),s[r]-'a');
            son(prv,s[r]-'a') = now;
        }
        return prv=now;
    }

    inline void clear(){
        for(register int i=0; i<=node_cnt; i++) fa(node_cnt) = len(node_cnt) = 0, memset(t[i].son,0,sizeof t[i].son);
        node_cnt = prv = 1;
        len(1) = -1; fa(0) = 1; fa(1) = 1;
    }
}

struct node{int son[26], fa, len;}t[MAXN<<1];
int node_cnt, prv;

inline int insert(char v){
    int x = ++node_cnt; len(x) = len(prv)+1;
    while(prv and !son(prv,v)) son(prv,v) = x, prv = fa(prv);
    if(!prv) fa(x) = 1;
    else{
        int p = son(prv,v);
        if(len(p) == len(prv)+1) fa(x) = p;
        else{
            int new_p = ++node_cnt; len(new_p) = len(prv)+1;
            fa(new_p) = fa(p); fa(p) = fa(x) = new_p;
            copy(t[p].son,t[p].son+26,t[new_p].son);
            while(prv and son(prv,v)==p) son(prv,v) = new_p, prv = fa(prv);
        }
    }
    return prv=x;
}

inline void clear(){
    PAM::clear();
    for(register int i=1; i<=node_cnt; i++) len(i) = fa(i) = 0, memset(t[i].son,0,sizeof t[i].son);
    node_cnt = prv = 1;
}

int n, ans;

int f[MAXN];
inline void solve(char *a, char *b){
    // printf("solve %s %s\n",a+1,b+1);
    clear();
    for(register int i=1; i<=n; i++) PAM::s[i] = a[n-i+1];
    for(register int i=1; i<=n; i++) f[n-i+1] = PAM::len(PAM::insert(i)); f[n+1] = 0;
    for(register int i=n; i>=1; i--) insert(b[i]-'a');
    int cur=1, nowlen=0;
    for(register int i=1; i<=n; i++){
        char v = a[i]-'a';
        while(cur and !son(cur,v)) cur = fa(cur), nowlen = min(nowlen,len(cur));
        if(!cur) cur = 1;
        else cur = son(cur,v), nowlen++, ans = max(ans,nowlen*2+f[i+1]);
    }
    // printf("ans=%d\n",ans);
}

char a[MAXN], b[MAXN];

int main(){
    // freopen("1.in","r",stdin);
    int T; Read(T);
    while(T--){
        scanf("%s%s",a+1,b+1);
        n = strlen(a+1); ans = -1;
        solve(a,b);
        for(register int i=1; i<n-i+1; i++) swap(a[i],a[n-i+1]), swap(b[i],b[n-i+1]);
        solve(b,a);
        cout<<ans<<'\n';
    }
    return 0;
}