HDU-6096 String(字典树+线段树扫描线)

传送门:HDU-6096

题解:字典树+线段树扫描线

首先用字典树对字符串按前缀的字典序排序,然后翻转字符串再按后缀的字典序排序,如果某些字符串要满足某个前缀,那么这些字符串一定是前缀排序中相邻,如果要满足后缀同理。

那么我们就可以得到2个区间[lx,rx],[ly,ry]分别代表满足前缀prf的字符串在[lx,rx]区间,满足后缀suf的字符串在[ly,ry]区间,如果某个字符串同时在这2个区间中则对答案的贡献就为1。

因此问题就可以变成平面矩形中包含多少个点,这就可以用线段树扫描线解决。将字符串在两种排序中的位置看成点(x,y),离线处理将矩形按横坐标从小到大排序,将点也按横坐标从小到大排序,最后扫一遍即可。


#include
#include
#include
#include
#include
#include
#define x first
#define y second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define FIN freopen("in.txt","r",stdin);
using namespace std;
typedef long long LL;
typedef pair PII;
const int MX = 1e5 + 5;
const int inf = 0x3f3f3f3f;
struct Trie {
    int nxt[26], v;
    void init() {
        memset(nxt, -1, sizeof(nxt));
        v = 0;
    }
} T[2][MX * 20], RT[MX * 20];
struct node {
    int x, y;
    bool operator<(const node& _A)const {
        if (x != _A.x) return x < _A.x;
        return y < _A.y;
    }
} p[MX];
struct Que {
    int id, type, x, l, r;
    string prf, suf;
    bool operator<(const Que& _A)const {
        return x < _A.x;
    }
} que[MX * 2];
string str[MX];
int n, q, sz, tot, rear;
int ans[MX], sum[MX << 2];
mapHash;


void insert(Trie T[], string str) {
    int tmp, now = 0;
    for (int i = 0; i < str.size(); i++) {
        tmp = str[i] - 'a';
        if (T[now].nxt[tmp] == -1) {
            T[++tot].init();
            T[now].nxt[tmp] = tot;
        }
        now = T[now].nxt[tmp];
        T[now].v++;
    }
}

void query(Trie T[], string str, int &l, int &r) {
    int now = 0, tmp, pre = n, cnt;
    l = 0;
    for (int i = 0; i < str.size(); i++) {
        tmp = str[i] - 'a';
        if (T[now].nxt[tmp] == -1) {
            l = r = inf;
            break;
        }
        cnt = 0;
        for (int j = tmp; j < 26; j++)
            if (T[now].nxt[j] != -1) cnt += T[T[now].nxt[j]].v;
        l += pre - cnt;
        r = l + T[T[now].nxt[tmp]].v - 1;
        now = T[now].nxt[tmp];
        pre = T[now].v;
    }
}
int find(Trie T[], string str) {
    int now = 0, tmp, pos = n - 1;
    for (int i = 0; i < str.size(); i++) {
        tmp = str[i] - 'a';
        for (int j = 25; j > tmp; j--) {
            if (T[now].nxt[j] == -1) continue;
            pos -= T[T[now].nxt[j]].v;
        }
        now = T[now].nxt[tmp];
    }
    for (int j = 0; j < 26; j++) {
        if (T[now].nxt[j] == -1) continue;
        pos -= T[T[now].nxt[j]].v;
    }
    return pos;
}
void PushUP(int rt) {
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void build(int l, int r, int rt) {
    sum[rt] = 0;
    if (l == r) return;
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
}
void update(int p, int l, int r, int rt) {
    if (l == r) {
        sum[rt]++;
        return;
    }
    int m = (l + r) >> 1;
    if (p <= m) update(p, lson);
    else update(p, rson);
    PushUP(rt);
}
int query(int L, int R, int l, int r, int rt) {
    if (L <= l && R >= r) return sum[rt];
    int m = (l + r) >> 1, ret = 0;
    if (L <= m) ret += query(L, R, lson);
    if (R > m) ret += query(L, R, rson);
    return ret;
}
void pre_solve() { //确定第i个字符串在前缀字典树和后缀字典树中的位置
    cin >> n >> q;
    T[0][0].init(); tot = 0;
    T[1][0].init(); rear = 0;
    Hash.clear();
    memset(ans,0,sizeof(ans));
    for (int i = 0; i < n; i++) {
        cin >> str[i];
        Hash[str[i]] = 1;
        insert(T[0], str[i]);
        reverse(str[i].begin(), str[i].end());
        insert(T[1], str[i]);
        reverse(str[i].begin(), str[i].end());
    }
    for (int i = 0; i < n; i++) {
        p[i].x = find(T[0], str[i]);
        reverse(str[i].begin(), str[i].end());
        p[i].y = find(T[1], str[i]);
        reverse(str[i].begin(), str[i].end());
    }
    Que t1, t2;
    sz = 0;
    string prf, suf;
    for (int i = 0; i < q; i++) {
        cin >> prf >> suf;
        t1.prf = prf; t1.suf = suf;
        reverse(suf.begin(), suf.end());
        t1.id = i; t1.type = -1;
        query(T[1], suf, t1.l, t1.r);
        t2 = t1; t2.type = 1;
        query(T[0], prf, t1.x, t2.x);
        if (t1.x == inf || t1.l == inf) continue;
        t1.x--;
        que[sz++] = t1; que[sz++] = t2;
    }
    sort(p, p + n);
    sort(que, que + sz);
    build(0, n - 1, 1);
}


int main() {
    //FIN;
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    int cas;
    cin >> cas;
    while (cas--) {
        pre_solve();
        for (int i = 0, j = 0; i < sz; i++) {
            if (que[i].x < 0) continue;
            while (p[j].x <= que[i].x && j < n) update(p[j].y, 0, n - 1, 1), j++;
            ans[que[i].id] += que[i].type * query(que[i].l, que[i].r, 0, n - 1, 1);
            string prf = que[i].prf, suf = que[i].suf;
            int sz1 = prf.size(), sz2 = suf.size();
            int mx = min(sz1, sz2);
            if (que[i].type == -1) continue;
            for (int k = 0; k < mx && prf[sz1 - 1 - k] == suf[k]; k++)
                if (Hash[prf.substr(0, sz1 - 1 - k) + suf])
                    ans[que[i].id] --;
        }
        for (int i = 0; i < q; i++) cout << ans[i] << endl;
    }
    return 0;
}


你可能感兴趣的:(字典树,线段树&树状数组)