相似度查询
普通解法
每次交换直接交换。每次查询都暴力匹配,存下现在可能的最长公共前缀长度len然后对于每个新串只要匹配前len个字符即可。
时间复杂度。显然这个时间复杂度不足以通过此题。
vector<int> solve(int n, vector<string>& s, int m, vector<int> &op, vector<int>& l, vector<int>& r) {
vector<int> ans;
for(int i = 0; i < m; ++i){
if(op[i] == 1){
if(l[i] == r[i]) continue;
else swap(s[l[i]-1], s[r[i]-1]);
continue;
}
if(l[i] == r[i]) ans.push_back(s[l[i]-1].size());
else{
int len = s[l[i]-1].size();
for(int j = l[i]; j < r[i] ; ++j){
len = min(len,(int) s[j].size());
for(int k = 0; k < len ; ++k){
if(s[j][k] != s[j-1][k]){
len = k; break;
}
}
}
ans.push_back(len);
}
}
return ans;
}最优解法
先对所有字符串建立一个字典树
考虑查询两个字符串的最长公共前缀的时候,相当于是查询它们对应的字典树结点在字典树上的公共祖先的深度。这就转化成了求树上最近公共祖先问题,可以通过树上倍增在的时间复杂度求出。
现在要求多个字符串的最长公共前缀,相当于求多个点在树上的最近公共祖先。按照dfs序把这些点排列之后,可以发现其实它们最近树上公共祖先是由dfs序最小的点和dfs序最大的点决定的。所以我们只要找出区间内DFS序最小的点和DFS序最大的点就可以了。
当需要交换字符串的时候,在线段树上修改对应位置的DFS序即可,而不用去操作串。
所以这题的解题步骤为:
- 建立字典树,求出每个字符串在字典树上对应的结点编号
- 从根结点开始DFS,求出倍增数组和整棵树的DFS序,求出每个结点对应的DFS序编号。
- 建立线段树维护区间最小值和区间最大值。[l,r]区间内维护的是[l,r]区间内的字符串对应的结点的最小/大DFS序。
- 查询的时候先在线段树上查询DFS序最小/最大的结点,然后求出它们的最近公共祖先,它的深度就是答案。
- 修改的时候,在线段树上修改对应位置的DFS序值,再交换两个字符串对应的结点编号。
需要倍增数组和字典树,所以空间复杂度为
求倍增数组复杂度为,单次线段树查询复杂度为
,单次LCA查询复杂度为
,总时间复杂度为
int ch[1000005][26], tot = 0;
int f[1000005][20];
int dfn[1000005], idx = 0, id[1000005], dep[1000005];
int to[1000005];
void dfs(int u){
if(!u) return;
for(int i = 1; i < 20; ++i) f[u][i] = f[f[u][i-1]][i-1];
dfn[++idx] = u; id[u] = idx;
for(int i = 0; i < 26; ++i) dfs(ch[u][i]);
}
int mi[1000005*4], mx[1000005*4];
void build(int rt, int l, int r){
if(l == r){
mx[rt] = mi[rt] = id[to[l]]; return;
}
int mid = (r+l)>>1;
build(rt<<1, l, mid);
build(rt<<1|1, mid+1, r);
mx[rt] = max(mx[rt<<1], mx[rt<<1|1]);
mi[rt] = min(mi[rt<<1], mi[rt<<1|1]);
return;
}
int qry(int rt, int l, int r, int L, int R, int op){//0 mi 1 mx
if(L <= l && r <= R)
return (op==1) ? mx[rt] : mi[rt];
int res; if(op == 0) res = 0x3f3f3f3f; else res = 0;
int mid = (r+l)>>1;
if(L <= mid) {
if(op == 0) res = min(res, qry(rt<<1, l, mid, L, R, op));
else res = max(res, qry(rt<<1, l, mid, L, R, op));
}
if(R > mid){
if(op == 0) res = min(res, qry(rt<<1|1, mid+1, r, L, R, op));
else res = max(res, qry(rt<<1|1, mid+1, r, L, R, op));
} return res;
}
void update(int rt, int l, int r, int pos, int x){
if(l == r){
mx[rt] = mi[rt] = x; return;
}
int mid = (r+l)>>1;
if(pos <= mid) update(rt<<1, l, mid, pos, x);
else update(rt<<1|1, mid+1, r, pos, x);
mx[rt] = max(mx[rt<<1], mx[rt<<1|1]);
mi[rt] = min(mi[rt<<1], mi[rt<<1|1]);
}
int lca(int u, int v){
if(dep[v] < dep[u]) swap(u, v);
int d = dep[v]-dep[u];
for(int i = 19; i >= 0; --i) if(d>>i&1) v = f[v][i];
if(u == v) return u;
for(int i = 19; i >= 0; --i){
if(f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
}return f[u][0];
}
int newnode(){
tot++;
memset(ch[tot], 0, sizeof ch[tot]); return tot;
}
vector<int> solve(int n, vector<string> &s, int m,vector<int> &op, vector<int> &l, vector<int> &r){
assert(s.size() == n);
idx = 0;
tot = 0; int rt = newnode(); dep[rt] = 0; f[rt][0] = 0;
for(int i = 0; i < s.size(); ++i){
int p = rt;
for(int j = 0; j < s[i].size(); ++j){
int x = s[i][j]-'a';
if(!ch[p][x]) ch[p][x] = newnode(), f[ch[p][x]][0] = p, dep[ch[p][x]] = dep[p]+1;
p = ch[p][x];
}
to[i+1] = p;
}
dfs(rt);
build(1, 1, n);
vector<int> ans; ans.clear();
assert(l.size() == r.size() && r.size() == m);
for(int i = 0; i < m; ++i){
if(op[i] == 1){
if(l[i] == r[i]) continue;
update(1, 1, n, l[i], id[to[r[i]]]);
update(1, 1, n, r[i], id[to[l[i]]]);
swap(to[l[i]], to[r[i]]);
continue;
}
assert(op[i] == 2);
assert(l[i] <= r[i] && l[i] > 0 && r[i] <= n);
int u = qry(1, 1, n, l[i], r[i], 0);u = dfn[u];
int v = qry(1, 1, n, l[i], r[i], 1);v = dfn[v];
int p = lca(u, v);
ans.push_back(dep[p]);
}return ans;
}