21.6.1 t1
阿新 • • 發佈:2021-06-25
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)))+(\)
然後你會發現那個 \(\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; }