eoj 3442

(后缀数组+树状数组)
题意:给定一个由英文字母构成的字符串 s(s<105) q(q<105) 个询问,每个询问包含一个区间 li,ri 和另一个字符串 zi ( |zi|<105 ),求 s[li,ri] 里出现了 zi 的次数?

(感觉这题出得很好啊,难点在于这个区间的限定条件)
思路:
  考虑使用后缀数组,这样对于所有查询串,能用 nlog(n) 的时间复杂度处理出每个查询串在原串出现的位置的上下界 sa[l] ~ sa[r] 。然后问题就转化为对于一个查询,求出这个上下界中有哪些后缀是在 [li,rilen(zi)+1] 范围里的(即求 sa[l] ~ sa[r] 里面有几个数字是在 [li,rilen(zi)+1] 范围)。
  接下来就可使用离线+树状数组处理。把每一个查询离线处理,按照 mx 值从小到大排序;同时将 sa[] 排序。然后处理 mx 询问时,依次将小于等于 mx sa[] 的数字从小到大插入树状数组中,再查询即可。
  总时间复杂度: O(nlog(n))

代码:

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;
const int maxn = 100010;

struct Num {
    int num, pos;
    Num(){}
    Num(int _num, int _pos):num(_num),pos(_pos){}
    bool operator < (const Num &A) const {
        return num < A.num;
    }
};
struct Query {
    int id, l, r, mx;
    Query(){}
    Query(int _id, int _l, int _r, int _mx):id(_id),l(_l),r(_r),mx(_mx){}
    bool operator < (const Query &A)const {
        return mx < A.mx;
    }
};
struct SuffixArray {
  int s[maxn];      // 原始字符数组(最后一个字符应必须是0,而前面的字符必须非0)
  int sa[maxn];     // 后缀数组
  int Rank[maxn];   // 名次数组. Rank[0]一定是n-1,即最后一个字符
  int height[maxn]; // height数组
  int t[maxn], t2[maxn], c[maxn]; // 辅助数组
  int n; // 字符个数

  void Clear() { n = 0; memset(sa, 0, sizeof(sa)); }

  // m为最大字符值加1。调用之前需设置好s和n
  void build_sa(int m) {
    int i, *x = t, *y = t2;
    for(i = 0; i < m; i++) c[i] = 0;
    for(i = 0; i < n; i++) c[x[i] = s[i]]++;
    for(i = 1; i < m; i++) c[i] += c[i-1];
    for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;
    for(int k = 1; k <= n; k <<= 1) {
      int p = 0;
      for(i = n-k; i < n; i++) y[p++] = i;
      for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i]-k;
      for(i = 0; i < m; i++) c[i] = 0;
      for(i = 0; i < n; i++) c[x[y[i]]]++;
      for(i = 0; i < m; i++) c[i] += c[i-1];
      for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
      swap(x, y);
      p = 1; x[sa[0]] = 0;
      for(i = 1; i < n; i++)
        x[sa[i]] = y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k] ? p-1 : p++;
      if(p >= n) break;
      m = p;
    }
  }

  void build_height() {
    int i, k = 0;
    for(i = 0; i < n; i++) Rank[sa[i]] = i;
    for(i = 0; i < n; i++) {
      if(k) k--;
      if(Rank[i] == 0) { height[Rank[i]] = 0; continue ; }
      int j = sa[Rank[i]-1];
      while(s[i+k] == s[j+k]) k++;
      height[Rank[i]] = k;
    }
  }
};

SuffixArray sa;
Num mynum[maxn];
Query qu[maxn<<1];
char s[maxn], z[maxn];
int ans[maxn<<1], tree[maxn];

int lowbit(int x) {
    return x&(-x);
}

void update(int pos, int num) {
    for(int i=pos; i<maxn; i+=lowbit(i))
        tree[i] += num;
}

int get_sum(int pos) {
    int ret = 0;
    for(int i=pos; i>0; i-=lowbit(i))
        ret += tree[i];
    return ret;
}

int cmp_suffix(char *pat, int p, int m) {
    return strncmp(pat, s + sa.sa[p], m);
}

void get_query(int id, int li, int ri, char *P) {
    int m = strlen(P);
    qu[2*id-1] = Query(2*id-1, -1, -1, -1);
    qu[2*id] = Query(2*id, -1, -1, -1);
    if (cmp_suffix(P, 0, m) < 0 || cmp_suffix(P, sa.n - 1, m) > 0)
        return ;
    int lb = 0, rb = 0;
    // get lower bound
    int l = 0, r = sa.n - 1;
    while(l < r) {
        int mid = (l+r)/2;
        if(cmp_suffix(P, mid, m) <= 0) r = mid;
        else l = mid + 1;
    }
    lb = l;
    // get upper bound
    l = 0, r = sa.n - 1;
    while (l < r) {
        int mid = (l+r+1)/2;
        if(cmp_suffix(P, mid, m) >= 0) l = mid;
        else r = mid - 1;
    }
    rb = l;
    //printf("lb: %d  rb : %d mx: %d %d\n",lb,rb,li,ri-m+1);
    if(rb - lb + 1 > 0) {
        qu[2*id-1] = Query(2*id-1, lb, rb, li-1);
        qu[2*id] = Query(2*id, lb, rb, ri-m+1);
    }
    return ;
}

int main() {
    while(scanf("%s",s) == 1) {
        int len = strlen(s);
        sa.Clear();
        for(int i=0; i<len; i++)
            sa.s[sa.n++] = s[i] - 'a' + 1;
        sa.s[sa.n++] = 0;
        sa.build_sa('z' - 'a' + 2);
        sa.build_height();
        for(int i=1; i<sa.n; i++)      // get number, 1 ~ sa.n-1
            mynum[i] = Num(sa.sa[i]+1, i);
        sort(mynum+1, mynum+sa.n);
        int q;
        scanf("%d",&q);
        for(int i=1; i<=q; i++) {      // get queries
            int li, ri;
            scanf("%d%d%s",&li,&ri,z);
            get_query(i, li+1, ri+1, z);
            //printf("ans : %d\n",ans);
        }
        sort(qu+1, qu+2*q+1);
        memset(tree, 0, sizeof(tree)); // initialize BIT
        int cur = 1;                   // solve
        for(int i=1; i<=2*q; i++) {
            while(cur < sa.n && mynum[cur].num <= qu[i].mx) {
                update(mynum[cur].pos, 1);
                cur ++;
            }
            if(qu[i].mx == -1)
                ans[qu[i].id] = 0;
            else {
                int tot = get_sum(qu[i].r) - get_sum(qu[i].l-1);
                ans[qu[i].id] = tot;
            }
        }
        for(int i=1; i<=q; i++)
            printf("%d\n",ans[2*i]-ans[2*i-1]);
    }
    return 0;
}
发布了40 篇原创文章 · 获赞 44 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/Site1997/article/details/78819270