trie树-- SP2881 CLONE - Find the Clones,CF633C Spy Syndrome 2
好像也讲不了什么,大致就是前缀相同的会合并到一起,不相同的出现分支
例题:
这道题可以一遍建树一遍查询,因为所有的单词都一样长,所以问题稍微简化了一点点,建树的时候记录一下以\(i\)为结尾的单词个数,边建树边查询,因为我们先查询到1次,才能查询到第2次,所以当我发现这个字符串出现了两次,那么出现一次的字符串就不包含当前这个字符,把出现两次的字符数量+1,出现一次的字符数量-1,其他情况同理,最后统计答案就好了。
code:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 20,maxm = 4e5+10;
int t,n,ans,cnt,m;
char s[maxn];
int trie[maxm][30];
int vis[maxm];
void build(char *a){
int len = strlen(a),root = 0,k=cnt;
for (int i = 0;i < len;i++){
int next = a[i]-'0';
if (!trie[root][next]) trie[root][next] = ++cnt;
root = trie[root][next];
ans += vis[root];
}
vis[root]++;
}
int num[maxm];
void init(){
memset(trie,0,sizeof(trie));
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
cnt = 0;
}
int main(){
while (scanf ("%d%d",&n,&m)&&(n+m)!=0){
init();
for (int i = 1;i <= n;i++){
scanf ("%s",s);
ans = 0;
build(s);
num[ans]++;if (ans) num[ans-1]--;
}
for (int i = 0;i <= n-1;i++) printf("%d\n",num[i]);
}
return 0;
}
我们可以正着先把\(Trie\)树建好,再倒着查询字符串,我们把每一个字母都当做是一个单词的开头查询一遍,当我们发现我们匹配到了一个完整的单词时,我们并不能保证他一定就是答案,因为可能这个单词和上一个匹配好的单词之间会有空余的字母没用上。这样的话,我们可以使用两个数组记录答案,\(lst_i\)数组表示以第\(i\)位字母结尾的单词,他的上一个首尾相接匹配好的单词末尾位置是\(lst_i\),\(ans_i\)表示以第\(i\)位字母结尾的单词的编号是\(ans_i\),递归输出就好
code:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1e6+10;
int n,m;
int ans[maxn],lst[maxn];
char s[maxn],c[1005];
int trie[maxn][30],vis[maxn];
int cnt;
string str[maxn];
int getnum(char x){
return x >= 'a'?x-'a':x-'A';
}
void build(int x,char *a){
int len = strlen(a),root = 0;
for (int i = 0;i < len;i++){
int to = getnum(a[i]);
if (!trie[root][to]) trie[root][to] = ++cnt;
root = trie[root][to];
if (i == len-1) vis[root] = x;
}
}
void query(int x){
int root = 0;
for (int i = x;i >= 0;i--){
int to = getnum(s[i]);
if (!trie[root][to]) return;
root = trie[root][to];
if (vis[root]&&(ans[i-1]||i==0)){
lst[x] = i-1;ans[x] = vis[root];return;
}
}
}
void print(int x){
if(lst[x] != -1) print(lst[x]);
cout<<str[ans[x]]<<" ";
}
int main(){
scanf ("%d",&n);
scanf ("%s",s);
scanf ("%d",&m);
for (int i = 1;i <= m;i++){
scanf ("%s",c);int len = strlen(c);
for (int j = 0;j < len;j++) str[i]+=c[j];
build(i,c);
}
for (int i = 0;i < n;i++){
query(i);
}
print(n-1);
return 0;
}