P4407 [JSOI2009] 电子字典
题目大意
给出 \(n\) 个互不相同字典串 \(S_i\)。和 \(m\) 个匹配串 \(Q_i\)。如果有字典串 \(S_i=Q_i\),输出 \(-1\)。三种变换操作:
- 在 \(S_{i,j}\) 后添加任意一个字符
- 删除 \(S_{i,j}\)
- 将 \(S_{i,j}\) 改成任意一个字符。
求每一个匹配串如果只进行 \(1\) 次操作有几个可以匹配的字典串。
\(n,m\leq 10^4\)
\(|S_i|,|Q_i|\leq 20\)
所有字符串都只有小写字母。
思路
考虑字典树。
发现时间复杂度猜一下是 \(O(26m|Q|)\) 的。其实发现哈希应该就能做,但是作者考场上没有写出来哈希,于是在这里补一个字典树做法。
先将所有字典串插入字典树,并记录结尾结点。对于每一个匹配串考虑深搜,用 \(f(u,l,c)\) 表示当前考虑到了字典树的 \(u\) 节点,是第 \(l\) 位,\(c\) 表示是否已经修改过。
如果已经修改过就一直到尾去记录是不是一个字典串。
之后对于三种操作:
添加任意一个字符就是对于 \(u\),进入到字符 \(x\) 中也就是搜索 \(f(t_{u,x},l,\text{true})\)。修改操作就是 \(f(t_{u,x},l+1,\text{true})\)。如果还能删就到 \(f_{t_{u,Q_{i,l}},l+1,\text{true}}\)。当然也可以选择这一位不修改。
代码
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const ll MAXN=1e5+5;
ll trie[MAXN*20][26],tot,ed[MAXN*20];
void insert(string s){
ll u=0;
for(auto c:s){
int x=c-'a';
if(!trie[u][x]){
trie[u][x]=++tot;
}
u=trie[u][x];
}
ed[u]=1;
}
string s;
bool vis[MAXN];
ll vu[MAXN],ans;
set<string>se;
void dfs(ll u,ll len,bool change){
if(len==s.size()&&ed[u]){
if(change&&!vis[u]){
vu[++ans]=u;
vis[u]=true;
}
return;
}
if(len>=s.size()){
if(!change){
for(int i=0;i<26;++i){
if(!trie[u][i]){continue;}
dfs(trie[u][i],len,true);
}
}
return;
}
ll x=s[len]-'a';
if(trie[u][x]){
dfs(trie[u][x],len+1,change);
}
if(!change){
dfs(u,len+1,true);
for(int i=0;i<26;++i){
if(!trie[u][i]){continue;}
dfs(trie[u][i],len,true);
dfs(trie[u][i],len+1,true);
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
ll n,Q;
cin>>n>>Q;
for(int i=1;i<=n;++i){
cin>>s;
se.insert(s);
insert(s);
}
while(Q--){
cin>>s;
if(se.count(s)){
cout<<-1<<endl;
continue;
}
ans=0;
dfs(0,0,false);
if(ans<0){
cout<<-1<<endl;
continue;
}
cout<<ans<<endl;
while(ans){
vis[vu[ans]]=false;
ans--;
}
}
return 0;
}