字典树:
一颗二十六叉树(只有小写(或大写)字母的话,数字是十叉,也有特殊的01字典树),
将串插入的时候,就按着儿子走,在最后结束位置打个标记(我一般开个标记数组)
感觉更像是一种思想。
关于清空的小技巧:
如果遇到多组输入了
tot清零(动态开点),把根的二十六个儿子清空,剩下的插入的时候,如果需要申请空间了,
先申请,然后清空所有儿子
可以降低时间复杂度
(对于某些特殊的oj,也可以降低空间复杂度,因为判断空间是操作的空间大小而不是开始时申请的空间大小)
统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀)
输入有些奇怪,别的没啥了
/*
author:revolIA
submit:;
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 3e5+7;
int Trie[maxn][30],tot,val[maxn];
void Insert(char *p,int pos = 0){
for(;*p!='\0';p++){
int to = *p-'a';
val[pos]++;
if(!Trie[pos][to])
Trie[pos][to] = ++tot;
pos = Trie[pos][to];
}
val[pos]++;
}
int Find(char *p,int pos = 0){
for(;*p!='\0';p++){
int to = *p-'a';
if(!Trie[pos][to])return 0;
pos = Trie[pos][to];
}
return val[pos];
}
char s[maxn];
int main(){
bool flag = true;
while(gets(s)){
if(s[0] == '\0'){
flag = false;
continue;
}
if(flag){
Insert(s);
}else{
printf("%d\n",Find(s));
}
}
return 0;
}
多组输入
一行多个单词,求这行不同的单词数
/*
author:revolIA
submit:;
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
int Trie[maxn][30],tot,val[maxn];
int Insert(char *p,int pos = 0){
int flag = 0;
for(;*p;p++){
int to = *p-'a';
if(!Trie[pos][to])
Trie[pos][to] = ++tot;
pos = Trie[pos][to];
}
if(!val[pos]){
flag = 1;
}
val[pos]++;
return flag;
}
char s[maxn],t[maxn];
int main(){
while(gets(s)){
if(s[0] == '#')break;
memset(val,0,sizeof(val));
memset(Trie,0,sizeof(Trie));
tot = 0;
int ans = 0,cnt = 0,i = 0;
while(s[i]){
while(s[i] && s[i] == ' ')i++;
while(s[i] && s[i] != ' ')
t[cnt++] = s[i++];
t[cnt] = '\0';
if(cnt)ans += Insert(t);
cnt = 0;
}
printf("%d\n",ans);
}
return 0;
}
给你一堆串,输出一些串x,x能被这堆串内两个串拼接成,即x=a+b,a,b在这堆串中
在想,有什么优雅的写法、结果tm的纯暴力拆解字符串、、
/*
author:revolIA
submit:;
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
int Trie[maxn][30],tot,val[maxn];
void Insert(char *p,int pos = 0){
for(;*p;p++){
int to = *p-'a';
if(!Trie[pos][to])
Trie[pos][to] = ++tot;
pos = Trie[pos][to];
}
val[pos]++;
}
bool Find(char *p,int pos = 0){
for(;*p;p++){
int to = *p-'a';
if(!Trie[pos][to])return false;
pos = Trie[pos][to];
}
return val[pos];
}
char s[maxn][50],t1[50],t2[50];
int main(){
int cnt = 0;
while(~scanf("%s",s[cnt]))Insert(s[cnt++]);
for(int i=0;i<cnt;i++){
int n = strlen(s[i]);
if(n == 1)continue;
for(int j=0;j<n-1;j++){
t1[j] = s[i][j];
t1[j+1] = '\0';
strcpy(t2,s[i]+j+1);
//printf("%s %s--->\n",t1,t2);
if(Find(t1) && Find(t2)){
printf("%s\n",s[i]);
break;
}
}
}
return 0;
}
给你一堆字符串,让你把他们简化为自己的某一个前缀,要求不会出现歧义
/*
author:revolIA
submit:;
*/
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
int Trie[maxn][30],tot,val[maxn];
void Insert(char *p,int pos = 0){
for(;*p;p++){
int to = *p-'a';
val[pos]++;
if(!Trie[pos][to])
Trie[pos][to] = ++tot;
pos = Trie[pos][to];
}
val[pos]++;
}
void Out(char *p,int pos = 0){
for(;*p;p++){
int to = *p-'a';
putchar(*p);
pos = Trie[pos][to];
if(val[pos] == 1)return;
}
}
char s[maxn][50];
int main(){
int cnt = 0;
while(~scanf("%s",s[cnt]))Insert(s[cnt++]);
for(int i=0;i<cnt;i++){
printf("%s ",s[i]),Out(s[i]),printf("\n");
}
return 0;
}
学会了个动态(大概)清空
题目问是否存在一个字符串是另一个字符串的前缀
那么直接插入字典树,考虑插入的字符串是较长的和较短的两种情况
维护一个路径的数组和一个结束点的数组
/*
author:revolIA
submit:;
*/
#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+7;
int Trie[maxn][10],tot,val[maxn],End[maxn],flag;
void Insert(char *p,int pos = 0){
for(;*p;p++){
int to = *p-'0';
val[pos]++;
if(!Trie[pos][to]){
Trie[pos][to] = ++tot;
for(int i=0;i<10;i++)Trie[tot][i] = 0;
}
pos = Trie[pos][to];
if(End[pos])flag = 0;//有个比自己短的
}
if(val[pos])flag = 0;//自己比别人短
val[pos]++;
End[pos]++;
}
char s[maxn];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
flag = 1;
while(n--){
scanf("%s",s);
if(flag)
Insert(s);
}
printf("%s\n",flag?"YES":"NO");
for(int i=0;i<10;i++)Trie[0][i] = 0;
memset(val,0,(tot+5)*sizeof(int));
memset(End,0,(tot+5)*sizeof(int));
tot = 0;
}
return 0;
}