题目
思路
其实想清楚了,就感觉这个题挺简单。没想清楚,就觉得很难。我就是想不清楚的那种啊。
首先前缀可以直接用 t r i e \tt trie trie 翻译过来。那么现在就是要计算 t r i e \tt trie trie 的子树内,后缀是 s 2 s_2 s2 的串的数量。感觉是可持久化 t r i e \tt trie trie 耶!——把反串拿出来建 t r i e \tt trie trie,然后一个点的 t r i e \tt trie trie 是其子树的合并结果。显然可以做到 O ( L 1 + L 2 ) \mathcal O(L_1+L_2) O(L1+L2) 的总复杂度。
而我们同样有一个很好写的做法:子树等价于 d f s \rm dfs dfs 序的一段区间。所以也可以正串、反串分别建 t r i e \tt trie trie 树,问题转化为了二维偏序。
在此题中,可以 O ( n 2 ) \mathcal O(n^2) O(n2) 暴力做二维前缀和,而后做到 O ( L 2 ) \mathcal O(L_2) O(L2) 的查询。喜欢主席树?可以做到 O ( n log n + m log n ) \mathcal O(n\log n+m\log n) O(nlogn+mlogn) 。喜欢分块?可以做到 O ( n n + m ) \mathcal O(n\sqrt{n}+m) O(nn+m) 。然而 O ( n + m n ) \mathcal O(n+m\sqrt{n}) O(n+mn) 难以持久化,我也不知道到底行不行。
空间复杂度是 O ( L 1 ⋅ ∣ Σ ∣ ) \mathcal O(L_1\cdot |\Sigma|) O(L1⋅∣Σ∣),感觉不是很好啊。路径压缩可以做到 O ( n ⋅ ∣ Σ ∣ + L 1 ) \mathcal O(n\cdot|\Sigma|+L_1) O(n⋅∣Σ∣+L1),相当于只建出了虚树。
代码
似乎因为那个
n
2
\sout{\;n^2\;}
n2的二维前缀和数组的存在,空间消耗仍然非常大呢。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int_;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(int x){
if(x > 9) writeint(x/10);
putchar((x-x/10*10)^48);
}
void caesar(char *p,int w){
for(; *p!='\0'; ++p)
*p = ((*p)-'a'+w)%26+'a';
}
const int MaxL = 2000005;
const int MaxN = 2005;
class Trie{
static const int MaxM = MaxN<<1;
int ch[MaxM][26], val[MaxM];
int cntNode, st[MaxM], ed[MaxM];
struct Node{
char c; Node* nxt;
Node(char C,Node* N){
c = C, nxt = N;
}
};
typedef Node* list;
list toList(char *p){
if(*p == '\0') return nullptr;
return new Node(*p,toList(p+1));
}
list path[MaxM][26];
int insert_on_chain(char* &p,list o,const int &end_of_chain){
for(; true; ++p,o=o->nxt){
if(o->nxt == nullptr) return 0; // run out
if(*p == '\0' || *p != o->nxt->c){
int x = ++ cntNode; // in the middle
ch[x][o->nxt->c-'a'] = end_of_chain;
path[x][o->nxt->c-'a'] = new Node(0,o->nxt);
o->nxt = nullptr; return x; // need father
}
}
return puts("THIS IS IMPOSSIBLE");
}
void insert(char *p,int id,int &o){
if(!o) o = ++ cntNode; // new Node
if(*p == '\0') return void(val[o] = id);
int &x = ch[o][*p-'a'];
if(x == 0){
path[o][*p-'a'] = new Node(0,toList(p));
x = ++ cntNode; // the leaf
return void(val[x] = id);
}
int y = insert_on_chain(p,path[o][*p-'a'],x);
if(y) x = y; // change child (split)
return insert(p,id,x); // go down
}
void giveId(int dfn[],int &o,int &now_v){
if(!o) return ; // empty subtree
st[o] = now_v+1; // not include now_v
if(val[o]) dfn[val[o]] = ++ now_v;
rep(i,0,25) giveId(dfn,ch[o][i],now_v);
ed[o] = now_v; // [st,ed]
}
int query_on_chain(char* &p,list &o,const int &end_of_chain){
if(o->nxt == nullptr) return 0; // run out
if(*p == '\0') return end_of_chain; // subtree
if(*p != o->nxt->c) return -1; // fail
return query_on_chain(++ p,o->nxt,end_of_chain);
}
pair<int,int> query(char *p,int &o){
if(!o) return make_pair(1,0); // empty
if(*p == '\0') // at the end
return make_pair(st[o],ed[o]);
int &x = ch[o][*p-'a'];
if(!x) return make_pair(1,0);
int y = query_on_chain(p,path[o][*p-'a'],x);
if(y == -1) return make_pair(1,0); // fail
if(y == 0) y = x; // continue matching
return query(p,y); // go down
}
int rt;
public:
Trie(){ rt = 0; }
void insert(char *p,int id){
return insert(p,id,rt);
}
void giveId(int dfn[]){
int xyx = 0; // alloc memory
return giveId(dfn,rt,xyx);
}
pair<int,int> query(char *p){
return query(p,rt);
}
};
Trie pre, suf;
int maze[MaxN][MaxN];
int p0[MaxN], p1[MaxN];
char str[MaxL];
int main(){
int n = readint();
rep(i,1,n){
scanf("%s",str);
pre.insert(str,i);
reverse(str,str+strlen(str));
suf.insert(str,i);
}
pre.giveId(p0), suf.giveId(p1);
rep(i,1,n) ++ maze[p0[i]][p1[i]];
rep(i,1,n) rep(j,1,n)
maze[i][j] += maze[i][j-1];
rep(j,1,n) rep(i,1,n)
maze[i][j] += maze[i-1][j];
for(int ans=0,m=readint(); m; --m){
scanf("%s",str); caesar(str,ans);
pair<int,int> rx = pre.query(str);
scanf("%s",str); caesar(str,ans);
reverse(str,str+strlen(str));
pair<int,int> ry = suf.query(str);
ans = maze[rx.second][ry.second]
- maze[rx.second][ry.first-1]
- maze[rx.first-1][ry.second]
+ maze[rx.first-1][ry.first-1];
printf("%d\n",ans);
}
return 0;
}