1. 程式人生 > >CF961F k-substrings

CF961F k-substrings

strings math In bstr ret input sub 長度 con

題意

給定一個字符串 \(S\)
求所有的 \(S[i,n-i+1]\)\(border\) 長度(最長的前綴等於後綴),要求長度是奇數
\(n\le 10^6\)

Sol

首先發現每次求的串都是原串去掉前後 \(i-1\) 位得到的串
一個套路,把串翻折,又因為 \(border\) 長度可能大於一半,所以我們把串倍長後翻折
也就是翻轉過來隔空插入在一起
例如:
\(bcabcabcabcabca\)
翻轉後 \(acbacbacbacbacb\)
隔一個插入在一起 \(baccabbaccabbaccabbaccabbaccab\)
那麽也就是求這個串的以某個位置的開始的最長回文串

又因為得到的這個串本身就是回文串,所以並不用翻轉過來,直接求以某個位置的結束的最長回文串就好了
比如 \(baccab\) 就是 \(S[1,3]\)\(S[13,15]\)
回文樹就好了

註意到每次都要跳 \(fail\) 鏈跳到滿足要求的位置,而每次都跳很耗時
如果之後跳到之前跳到過的點,就可以直接跳到之前跳到的對答案有貢獻的點上
再繼續跳
並查集維護一下就好了

# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std; typedef long long ll; IL int Input(){ RG int x = 0, z = 1; RG char c = getchar(); for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1; for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3
) + (c ^ 48); return x * z; } const int maxn(2e6 + 5); int n, last, tot, anc[maxn], id[maxn]; int len[maxn], first[maxn], nxt[maxn], type[maxn], fa[maxn]; char s[maxn], str[maxn]; IL int Son(RG int u, RG int c){ for(RG int v = first[u]; v; v = nxt[v]) if(type[v] == c) return v; return 0; } IL void Link(RG int u, RG int v, RG int c){ nxt[v] = first[u], first[u] = v, type[v] = c; } IL void Extend(RG int pos, RG int c){ RG int p = last; while(s[pos - len[p] - 1] != s[pos]) p = fa[p]; if(!Son(p, c)){ RG int np = ++tot, q = fa[p]; while(s[pos - len[q] - 1] != s[pos]) q = fa[q]; len[np] = len[p] + 2, fa[np] = Son(q, c); Link(p, np, c); } last = Son(p, c); } IL int Find(RG int x){ return anc[x] == x ? x : anc[x] = Find(anc[x]); } int main(){ Fill(type, -1), n = Input(), scanf(" %s", str + 1); for(RG int t = 0, i = 1, j = n; j; ++i, --j) s[++t] = str[i], s[++t] = str[j]; tot = 1, fa[0] = fa[1] = 1, len[1] = -1, n <<= 1, anc[0] = 1; for(RG int i = 1; i <= n; ++i) Extend(i, s[i] - 'a'), id[i] = last; for(RG int i = 0; i <= tot; ++i) anc[i] = i; for(RG int i = 1, m = n >> 1, t = (m + 1) >> 1; i <= t; ++i){ RG int x = Find(id[n - ((i - 1) << 1)]); while(x != 1 && ((len[x] >> 1) >= (m - ((i - 1) << 1)) || len[x] % 4 != 2)) x = anc[x] = Find(fa[anc[x]]); printf("%d ", (len[x] % 4 == 2) ? (len[x] >> 1) : -1); } return 0; }

CF961F k-substrings