传送门:HDU-6096
题解:字典树+线段树扫描线
首先用字典树对字符串按前缀的字典序排序,然后翻转字符串再按后缀的字典序排序,如果某些字符串要满足某个前缀,那么这些字符串一定是前缀排序中相邻,如果要满足后缀同理。
那么我们就可以得到2个区间[lx,rx],[ly,ry]分别代表满足前缀prf的字符串在[lx,rx]区间,满足后缀suf的字符串在[ly,ry]区间,如果某个字符串同时在这2个区间中则对答案的贡献就为1。
因此问题就可以变成平面矩形中包含多少个点,这就可以用线段树扫描线解决。将字符串在两种排序中的位置看成点(x,y),离线处理将矩形按横坐标从小到大排序,将点也按横坐标从小到大排序,最后扫一遍即可。
#include<iostream>
#include<string.h>
#include<algorithm>
#include<cstdio>
#include<string>
#include<map>
#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<int, int> 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];
map<string, bool>Hash;
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;
}