题目大意
详细题目传送门
给出 \(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;
}