LOJ 2720 & UOJ 395 & BZOJ 5417 「NOI2018」你的名字 後綴自動機 線段樹合並
LOJ #2720 UOJ #395 BZOJ 5417
題意
給出字符串S,有Q次詢問,每次給出字符串T和整數\(l,r\)滿足\(1\le l\le r\le|S|\)
求T有多少個本質不同的(連續)子串滿足不是\(S[l..r]\)的(連續)子串
只含小寫字母
68%:沒有l,r的限制
同步賽時這都不會
對S建SAM,對每個T建SAM
對於SAM上一個節點u,令\(len_u\)表示這個節點能表示的最長的字符串,\(fa_u\)表示u在parent樹上的父親/fail指針,\(g_u\)表示節點u的right集合中的任意一個位置。令\(d_i\)表示\(T[1..i]\)的極長的在S中出現的後綴的長度。
T含有的本質不同的子串數是\(\sum len_u-len_{fa_u}\),但是由於不能在S中出現,通過觀察可以知道所求即為\(\sum max(0,len_u-max(len_{fa_u},d_{g_u}))\)。因為節點u能表示的字符串長度要大於\(len_{fa_u}\),而且這些字符串是後綴關系,有且僅有長度\(\le d_{g_u}\)的串在S中出現。
\(d\)可以由把T放到S的SAM上跑一下得到,具體是沒有轉移就跳fail直到有轉移,同時維護匹配長度,即\(d\)。
之後枚舉T的SAM上的點就可以計算了
時間復雜度\(O(\sum|T|+|S|)\),代碼在下方
100%
現在有了一個區間的限制,這只會影響\(d\)
有轉移不能直接走,而需要判斷轉移後的節點是否可以在[l..r]內完整表示新匹配串
這裏需要每次將匹配長度-1,而不是每次跳一下fail(我沒這麽寫過)
設當前匹配長度為k,問題可以轉化為判斷轉移後的節點的right集合中是否有[l+k,r]中的元素,使用線段樹合並實現。
時間復雜度\(O((\sum|T|+|S|)log|S|)\)
68%
#include<cstdio> #include<algorithm> #include<ctype.h> #include<string.h> #include<math.h> using namespace std; #define ll long long inline char read() { static const int IN_LEN = 1000000; static char buf[IN_LEN], *s, *t; return (s == t ? t = (s = buf) + fread(buf, 1, IN_LEN, stdin), (s == t ? -1 : *s++) : *s++); } template<class T> inline void read(T &x) { static bool iosig; static char c; for (iosig = false, c = read(); !isdigit(c); c = read()) { if (c == '-') iosig = true; if (c == -1) return; } for (x = 0; isdigit(c); c = read()) x = ((x + (x << 2)) << 1) + (c ^ '0'); if (iosig) x = -x; } const int OUT_LEN = 10000000; char obuf[OUT_LEN], *ooh = obuf; inline void print(char c) { if (ooh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), ooh = obuf; *ooh++ = c; } template<class T> inline void print(T x) { static int buf[30], cnt; if (x == 0) print('0'); else { if (x < 0) print('-'), x = -x; for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48; while (cnt) print((char)buf[cnt--]); } } inline void flush() { fwrite(obuf, 1, ooh - obuf, stdout); } const int N = 500005; int n, m, q, d[N]; char s[N], t[N]; struct sam{ int n, cnt, last, g[N<<1], fa[N<<1], len[N<<1], ch[N<<1][26]; sam(){ n=0, cnt=last=1;} inline void extend(int c){ int p=last, np=++cnt; last=np, len[np]=len[p]+1; while(p && !ch[p][c]) ch[p][c]=np, p=fa[p]; if(!p) fa[np]=1; else{ int q=ch[p][c]; if(len[q]==len[p]+1) fa[np]=q; else{ int nq=++cnt; len[nq]=len[p]+1, memcpy(ch[nq], ch[q], 26<<2); fa[nq]=fa[q], fa[q]=fa[np]=nq; while(ch[p][c]==q) ch[p][c]=nq, p=fa[p]; } } g[np]=++n; } inline void clear(){ memset(fa+1, 0, cnt<<2), memset(len+1, 0, cnt<<2), memset(g+1, 0, cnt<<2); for(int i=1; i<=cnt; ++i) memset(ch[i], 0, 26<<2); n=0, cnt=last=1; } inline ll solve(int *d){ static int b[N], a[N<<1]; memset(b+1, 0, n<<2); for(int i=1; i<=cnt; ++i) ++b[len[i]]; for(int i=1; i<=n; ++i) b[i]+=b[i-1]; for(int i=1; i<=cnt; ++i) a[b[len[i]]--]=i; ll ans=0; for(int i=cnt; i; --i){ int u=a[i]; g[fa[u]]=g[u]; ans+=max(0, len[u]-max(len[fa[u]], d[g[u]])); } return ans; } }S, T; int main() { while(isalpha(s[++n]=read())); --n; for(int i=1; i<=n; ++i) S.extend(s[i]-'a'); read(q); while(q--){ while(isspace(t[1]=read())); m=1; while(isalpha(t[++m]=read())); --m; int tmp; read(tmp), read(tmp); for(int i=1, p=1, len=0; i<=m; ++i){ int c=t[i]-'a'; T.extend(c); if(S.ch[p][c]) p=S.ch[p][c], ++len; else{ while(p && !S.ch[p][c]) p=S.fa[p]; if(!p) p=1, len=0; else len=S.len[p]+1, p=S.ch[p][c]; } d[i]=len; } print(T.solve(d)), print('\n'); T.clear(); } return flush(), 0; }
100%
#include<cstdio>
#include<algorithm>
#include<ctype.h>
#include<string.h>
#include<math.h>
using namespace std;
#define ll long long
inline char read() {
static const int IN_LEN = 1000000;
static char buf[IN_LEN], *s, *t;
return (s == t ? t = (s = buf) + fread(buf, 1, IN_LEN, stdin), (s == t ? -1 : *s++) : *s++);
}
template<class T>
inline void read(T &x) {
static bool iosig;
static char c;
for (iosig = false, c = read(); !isdigit(c); c = read()) {
if (c == '-') iosig = true;
if (c == -1) return;
}
for (x = 0; isdigit(c); c = read()) x = ((x + (x << 2)) << 1) + (c ^ '0');
if (iosig) x = -x;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN], *ooh = obuf;
inline void print(char c) {
if (ooh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), ooh = obuf;
*ooh++ = c;
}
template<class T>
inline void print(T x) {
static int buf[30], cnt;
if (x == 0) print('0');
else {
if (x < 0) print('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) print((char)buf[cnt--]);
}
}
inline void flush() { fwrite(obuf, 1, ooh - obuf, stdout); }
const int N = 500005, M = N*21*2;
int n, m, q, cnt, d[N], b[N], a[N<<1], rt[N<<1], lson[M], rson[M];
char s[N], t[N];
void insert(int l, int r, int &t, int x){
if(!t) t=++cnt;
if(l==r) return;
int mid=l+r>>1;
if(x<=mid) insert(l, mid, lson[t], x); else insert(mid+1, r, rson[t], x);
}
int Merge(int x, int y){
if(!x || !y) return x|y;
int tmp=++cnt;
lson[tmp]=Merge(lson[x], lson[y]), rson[tmp]=Merge(rson[x], rson[y]);
return tmp;
}
bool exist(int l, int r, int t, int L, int R){
if(!t) return 0;
if(L<=l && r<=R) return 1;
int mid=l+r>>1;
if(L<=mid && exist(l, mid, lson[t], L, R)) return 1;
return R>mid && exist(mid+1, r, rson[t], L, R);
}
struct sam{
int n, cnt, last, g[N<<1], fa[N<<1], len[N<<1], ch[N<<1][26];
sam(){ cnt=last=1;}
inline void extend(int c){
int p=last, np=++cnt;
last=np, len[np]=len[p]+1;
while(p && !ch[p][c]) ch[p][c]=np, p=fa[p];
if(!p) fa[np]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else{
int nq=++cnt;
len[nq]=len[p]+1, memcpy(ch[nq], ch[q], 26<<2);
fa[nq]=fa[q], fa[q]=fa[np]=nq;
while(ch[p][c]==q) ch[p][c]=nq, p=fa[p];
}
}
g[np]=++n;
}
inline void clear(){
memset(fa+1, 0, cnt<<2), memset(len+1, 0, cnt<<2), memset(g+1, 0, cnt<<2);
for(int i=1; i<=cnt; ++i) memset(ch[i], 0, 26<<2);
n=0, cnt=last=1;
}
inline ll solve(int *d){
memset(b+1, 0, n<<2);
for(int i=1; i<=cnt; ++i) ++b[len[i]];
for(int i=1; i<=n; ++i) b[i]+=b[i-1];
for(int i=1; i<=cnt; ++i) a[b[len[i]]--]=i;
ll ans=0;
for(int i=cnt; i; --i){
int u=a[i];
g[fa[u]]=g[u];
ans+=max(0, len[u]-max(len[fa[u]], d[g[u]]));
}
return ans;
}
inline void init(){
memset(b+1, 0, n<<2);
for(int i=1; i<=cnt; ++i) ++b[len[i]];
for(int i=1; i<=n; ++i) b[i]+=b[i-1];
for(int i=1; i<=cnt; ++i) a[b[len[i]]--]=i;
for(int i=cnt; i; --i){
int u=a[i];
if(g[u]) insert(1, n, rt[u], g[u]);
rt[fa[u]]=Merge(rt[fa[u]], rt[u]);
}
}
}S, T;
int main() {
while(isalpha(s[++n]=read()));
--n;
for(int i=1; i<=n; ++i) S.extend(s[i]-'a');
S.init();
read(q);
while(q--){
while(isspace(t[1]=read()));
m=1;
while(isalpha(t[++m]=read()));
--m;
int l, r;
read(l), read(r);
for(int i=1, p=1, len=0; i<=m; ++i){
int c=t[i]-'a';
T.extend(c);
while(1){
if(S.ch[p][c] && exist(1, n, rt[S.ch[p][c]], l+len, r)){
p=S.ch[p][c], ++len;
break;
}
if(!len) break;
if(--len==S.len[S.fa[p]]) p=S.fa[p];
}
d[i]=len;
}
print(T.solve(d)), print('\n');
T.clear();
}
return flush(), 0;
}
LOJ 2720 & UOJ 395 & BZOJ 5417 「NOI2018」你的名字 後綴自動機 線段樹合並