1. 程式人生 > >BZOJ4556: [Tjoi2016&Heoi2016]字符串

BZOJ4556: [Tjoi2016&Heoi2016]字符串

兩個 name 長度 upd 前綴 memory ons desc 表示

題解: 首先對於原串建SAM 我們考慮對於每次查詢區間[c,d] 我們考慮二分答案 然後我們只需判定[a,b]中是否會產生這個子串即可 首先我們可以倍增在parent樹上找到包含這個子串的節點 然後只需判斷其子樹中葉子節點是否出現在[a-mid+1,b]中 這個可以通過線段樹合並來實現即可

/**************************************************************
    Problem: 4556
    User: c20161007
    Language: C++
    Result: Accepted
    Time:9004 ms
    Memory:92912 kb
****************************************************************/
 
#include <bits/stdc++.h>
const int MAXN=2e5+10;
#define link(x) for(edge *j=h[x];j;j=j->next)
using namespace std;
int dis[MAXN],fa[MAXN][21],ch[MAXN][26],vis[MAXN];
int cur,cnt,rt,pos[MAXN],n,m;
char str[MAXN];
struct edge{int t;edge*next;}e[MAXN],*h[MAXN],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
int Rt[MAXN];
void built(int x,int id){
    int last=cur;cur=++cnt;int p=last;dis[cur]=id;vis[cur]=1;pos[id]=cur;
    for(;p&&!ch[p][x];p=fa[p][0])ch[p][x]=cur;
    if(!p)fa[cur][0]=rt;
    else{
        int q=ch[p][x];
        if(dis[q]==dis[p]+1)fa[cur][0]=q;
        else{
            int nt=++cnt;dis[nt]=dis[p]+1;
            memcpy(ch[nt],ch[q],sizeof(ch[q]));
            fa[nt][0]=fa[q][0];fa[q][0]=fa[cur][0]=nt;
            for(;ch[p][x]==q;p=fa[p][0])ch[p][x]=nt;
        }
    }
}
typedef struct node{int l,r,sum;}node;
node d[MAXN*21];int cnt1;
void merge(int &x,int y,int l,int r){
    if(!y)return ;
    if(!x){x=y;return ;}
    int t=++cnt1;d[t]=d[x];d[t].sum+=d[y].sum;x=t;
    if(l==r)return ;
    int mid=(l+r)>>1;
    merge(d[x].l,d[y].l,l,mid);
    merge(d[x].r,d[y].r,mid+1,r);
}
void update(int &x,int l,int r,int t){
    if(!x)x=++cnt1;
    d[x].sum++;
    if(l==r)return ;
    int mid=(l+r)>>1;
    if(t<=mid)update(d[x].l,l,mid,t);
    else update(d[x].r,mid+1,r,t);
}
int a,b,ans1;
void querty(int x,int l,int r,int ql,int qr){
    if(!x)return;
    if(ql<=l&&r<=qr){ans1+=d[x].sum;return ;}
    int mid=(l+r)>>1;
    if(ql<=mid)querty(d[x].l,l,mid,ql,qr);
    if(qr>mid)querty(d[x].r,mid+1,r,ql,qr);
}
void dfs(int x,int pre){
    for(int i=1;i<=20;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    link(x){
        if(j->t!=pre){
            dfs(j->t,x);
            merge(Rt[x],Rt[j->t],1,n);
        }
    }
    if(vis[x])update(Rt[x],1,n,dis[x]);
    ans1=0;querty(Rt[6],1,n,1,2);
}
bool check(int mid,int c){
    int t=c+mid-1;int x=pos[t];
    for(int i=20;i>=0;i--){
        if(dis[fa[x][i]]>=mid)x=fa[x][i];
    }
    if(a+mid-1>b)return false;
    ans1=0;querty(Rt[x],1,n,a+mid-1,b);
    if(ans1>0)return true;
    return false;
}
int main(){
    scanf("%d%d",&n,&m);
    scanf("%s",str+1);rt=cnt=cur=1;cnt1=0;
    for(int i=1;i<=n;i++)built(str[i]-‘a‘,i);
    for(int i=1;i<=cnt;i++)add(fa[i][0],i);
    dfs(rt,0);int l1,r1,l2,r2;
    while(m--){
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        int l=1;int r=r2-l2+1;int ans=0;a=l1;b=r1;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid,l2))ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

4556: [Tjoi2016&Heoi2016]字符串

Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 1563 Solved: 627
[Submit][Status][Discuss]

Description

佳媛姐姐過生日的時候,她的小夥伴從某東上買了一個生日禮物。生日禮物放在一個神奇的箱子中。箱子外邊寫了 一個長為n的字符串s,和m個問題。佳媛姐姐必須正確回答這m個問題,才能打開箱子拿到禮物,升職加薪,出任CE O,嫁給高富帥,走上人生巔峰。每個問題均有a,b,c,d四個參數,問你子串s[a..b]的所有子串和s[c..d]的最長公 共前綴的長度的最大值是多少?佳媛姐姐並不擅長做這樣的問題,所以她向你求助,你該如何幫助她呢?

Input

輸入的第一行有兩個正整數n,m,分別表示字符串的長度和詢問的個數。接下來一行是一個長為n的字符串。接下來 m行,每行有4個數a,b,c,d,表示詢問s[a..b]的所有子串和s[c..d]的最長公共前綴的最大值。1<=n,m<=100,000, 字符串中僅有小寫英文字母,a<=b,c<=d,1<=a,b,c,d<=n

Output

對於每一次詢問,輸出答案。

Sample Input

5 5
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4

Sample Output

1
1
2
2
2

BZOJ4556: [Tjoi2016&Heoi2016]字符串