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;
}

转载于:https://www.cnblogs.com/CMXRYNP/p/9736025.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值