题解:CF566A Matching Names
题意
有
思路
把笔名插入字典树,并在每个结束点
然后枚举每个人的真名,找字典树中所有笔名中的最长公共前缀,在最长前缀的末尾
最后遍历一遍字典树,进行后序遍历,若搜索至位置
但存在一定的问题,如果已经搜索了子树
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 8e5 + 5 , M = 2e5 + 5;
int n;
string s[M];
int trie[N][27] , tot , ans[M] , sta[M] , top , h = 0;
vector<int>w[N] , vis[N];
void insert(string x , int val) {
int i = 0;
for(auto v : x) {
int k = v - 'a';
if(!trie[i][k]) trie[i][k] = ++ tot;
i = trie[i][k];
}
vis[i].push_back(val); // 标记位置 $i$ 到根组成的字符串是某个同学的笔名。
}
void find(string x , int val) {
int i = 0;
for(auto v : x) {
int k = v - 'a';
if(!trie[i][k]) break;
i = trie[i][k];
}
w[i].push_back(val); // 标记位置 $i$ 到根组成的字符串是某个同学的真名与所有笔名的最长公共前缀。
}
void dfs(int i , int fa , int dep , int tp) {
for(auto v : vis[i]) {
sta[++ top] = v; // 把当前位置的笔名标记加入栈中
}
for(int v = 0 ; v < 26 ; v ++) {
if(!trie[i][v]) continue;
dfs(trie[i][v] , i , dep + 1 , top);
}
for(int j = w[i].size() - 1 ; j >= 0 ; j --) {
int v = w[i][j];
if(top - tp > 0) { // 若栈不为空
ans[v] = sta[top --]; // 匹配
h += dep; // 计算答案
} else { // 若栈为空
w[fa].push_back(v); // 上放到其父节点中
}
w[i].pop_back();
}
}
int main() {
cin>>n;
for(int i = 1 ; i <= n + n ; i ++) cin>>s[i];
for(int i = n + 1 ; i <= n + n ; i ++) insert(s[i] , i - n); // 把笔名插入字典树
for(int i = 1 ; i <= n ; i ++) find(s[i] , i); // 找每个真名与所有笔名中的最长公共前缀
dfs(0 , 0 , 0 , 0);
cout<<h<<'\n';
for(int i = 1 ; i <= n ; i ++) {
cout<<i<<' '<<ans[i]<<'\n';
}
return 0;
}