HDU #5507 GT and Strings

这是AC自动机系列的第一篇

传送门

           Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)0

大意

给出N个仅由小写字母组成的字符串S[1]...S[N],它们的总长度为L。有Q组询问,询问分两类:

1.S[x]是否是S[y]的子序列;

2.S[x]是否是S[y]的子串。

数据范围:

N,L,Q<=100000,有60%的数据满足 L<=100, Q<=1000


Solution

先贴代码,留坑待填。
开始填坑。
首先由于这题的数据范围比较坑,不大可能开二维数组来存字符串,故采用了将各字符串连续存起来的办法,另开一个数组记录各串首字母位置。
void input(int n){
    for(int i=0; i<n; i++){
        scanf("%s", s+beg[i]);
        beg[i+1]=beg[i]+strlen(s+beg[i]); 
    }
}
判断子序列询问比较简单,可以O(N)解决。
nt[i][j]表示位置第j个字母在位置i之后(包括位置i)首次出现的位置。从后往前扫一遍就可以打出这个表了。
int nt[N][26];
void calc_nt(int n){
    memset(nt, -1, 26*beg[n]<<2);
    for(int i=0, j; i<n; i++){
        j=beg[i+1]-1;
        nt[j][s[j]-'a']=j;
        for(j--; j>=beg[i]; j--)
            for(int k=0; k<26; k++)
                if(k==s[j]-'a') nt[j][k]=j;
                else nt[j][k]=nt[j+1][k];
    }
}

查询时只要在串S[j]对应的nt表中不断往后匹配就可以了:

int subseq(int x, int y){
    if(len(x)>len(y)) return 0;
    for(int i=beg[x], j=beg[y]; i<beg[x+1]; i++){
        if(j>=beg[y+1]) return 0;
        if(nt[j][s[i]-'a']==-1) return 0;
        j=nt[j][s[i]-'a']+1;
    }
    return 1;
}

判断子串的询问可用AC自动机离线处理。

 

AC代码

#include <bits/stdc++.h>
using namespace std;

const int N(1e5+5);
int beg[N], id[N];
char s[N];

void input(int n){
    for(int i=0; i<n; i++){
        scanf("%s", s+beg[i]);
        beg[i+1]=beg[i]+strlen(s+beg[i]); 
    }
}

int x[N], y[N];
void preprocess(int q, map<pair<int,int>,int> &mp){
    mp.clear();
    for(int i=0; i<q; i++){
        scanf("%d%d", x+i, y+i), x[i]--, y[i]--;
        mp[{id[x[i]], id[y[i]]}];
    }
}

int nt[N][26];
void calc_nt(int n){
    memset(nt, -1, 26*beg[n]<<2);
    for(int i=0, j; i<n; i++){
        j=beg[i+1]-1;
        nt[j][s[j]-'a']=j;
        for(j--; j>=beg[i]; j--)
            for(int k=0; k<26; k++)
                if(k==s[j]-'a') nt[j][k]=j;
                else nt[j][k]=nt[j+1][k];
    }
}

int len(int x){return beg[x+1]-beg[x];}

int subseq(int x, int y){
    if(len(x)>len(y)) return 0;
    for(int i=beg[x], j=beg[y]; i<beg[x+1]; i++){
        if(j>=beg[y+1]) return 0;
        if(nt[j][s[i]-'a']==-1) return 0;
        j=nt[j][s[i]-'a']+1;
    }
    return 1;
}

void solve1(int q, map<pair<int,int>,int> &mp, int *ans){
    mp.clear();
    for(int i=0; i<q; i++){
        if(mp.find({x[i], y[i]})==mp.end())
            mp[{x[i], y[i]}]=subseq(x[i], y[i]);
        ans[i]=mp[{x[i], y[i]}];
    }
}

int ch[N][26], f[N], last[N], val[N];
void init(int i){
    memset(ch[i], 0, sizeof(ch[i]));
    f[i]=last[i]=val[i]=0;
}

void build_trie(int n){
    int tot=0; init(tot++);
    for(int i=0, ID=0, u; i<n; i++){
        for(int j=(u=0,beg[i]); j<beg[i+1]; j++){
            int &v=ch[u][s[j]-'a'];
            if(!v) v=tot++, init(v);
            u=v;
        }
        if(!val[u]) val[u]=++ID;
        id[i]=val[u];
    }
}

int que[N];
int build_ac(){
    int head=0, tail=0;
    for(int i=0; i<26; i++){
        if(ch[0][i]) que[tail++]=ch[0][i];
    }
    for(int u; head!=tail;){
        u=que[head++];
        for(int i=0; i<26; i++){
            int &v=ch[u][i];
            if(v){
                f[v]=ch[f[u]][i];
                last[v] = val[f[v]] ? f[v] : last[f[v]];
                que[tail++]=v;
             }
            else v=ch[f[u]][i];
        }
    }
}

void solve2(int n, int q, map<pair<int,int>,int> &mp, int *ans){
    for(int i=0, x, y; i<n; i++){
        y=id[i];
        for(int j=beg[i], k=0; j<beg[i+1]; j++){
            k=ch[k][s[j]-'a'];
            x=val[k];
            if(x&& mp.find({x, y})!=mp.end())
                mp[{x,y}]=1;
            for(int l=last[k]; l; l=last[l]){
                x=val[l];
                if(mp.find({x, y})!=mp.end())
                    mp[{x,y}]=1;
            }
        }
    }
    for(int i=0; i<q; i++) 
        ans[i]=mp[{id[x[i]],id[y[i]]}];
}
int ans[2][N];
map<pair<int,int>,int> mp[2];
int main(){
    int T; scanf("%d", &T);
    for(int n, q; T--; puts("")){
        scanf("%d%d", &n, &q);
        input(n);
        calc_nt(n);
        build_trie(n);
        build_ac();
        preprocess(q, mp[1]);
        solve1(q, mp[0], ans[0]);
        solve2(n, q, mp[1], ans[1]);
        for(int i=0; i<q; i++) printf("%d%d", ans[0][i], ans[1][i]);
    }
}

 

posted @ 2016-04-20 18:17  Pat  阅读(315)  评论(0编辑  收藏  举报