Trie字典树
loj.ac 10049
代码1:Trie树+指针实现
时间复杂度:O(TN10),每个字符串长度最大为10,在树上插入一个字符串最多10步。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int T, flag; //flag为有前缀标记
char s[11];
struct node{ int end; node *nxt[10]; node(){end=0; memset(nxt, 0, sizeof(nxt));} }*root;//root为根指针,使用链式前向星会降低查找效率
void insert(char *s)
{
bool ret=false; node *r=root; int l=strlen(s);
for(int i=0; i<l; i++)
{
int id=s[i]-'0';
if(r->nxt[id]==NULL) r->nxt[id]=new node();
else if(i==l-1 && r->nxt[id]->end==0) flag=1; //最后一个字符结点已经存在且没有结束标记,说明s是某字符串的前缀
r=r->nxt[id];
if(i<l-1 && r->end) flag=1; //走过的路径上(当前s还未结束)发现了结束标记,存在前缀
if(i==l-1) r->end=1; //打单词结束标记
}
}
void clearTree(node * &t) //递归清除树
{
if(!t) return;
for(int i=0; i<10; i++)
if(t->nxt[i]) clearTree(t->nxt[i]);
delete t;
t=NULL;
}
int main()
{
scanf("%d", &T);
while(T--)
{
root=new node();
flag=0;
int n;
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%s", s);
insert(s);
}
if(flag) printf("NO\n");
else printf("YES\n");
clearTree(root); //清空树
}
return 0;
}
代码2:sort+贪心
时间复杂度:O(T(NlgN+10N)),排序为NlgN,每个字符串长度最大为10,每次查找字串最多10步,共查找N-1次。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
string s[10001];
int main()
{
int T, n;
cin>>T;
while(T--)
{
bool flag=false;
cin>>n;
for(int i=1; i<=n; i++) cin>>s[i];
sort(s+1, s+n+1); //排序所有字符串
for(int i=1; i<=n-1; i++)
{
if(s[i+1].find(s[i], 0)==0) //后一项中查找前一项是否是其子串,这里使用find会带来性能的降低,find会从0号位置之后的所有可能位置进行匹配,建议手打查找,仅从0号位置匹配。
{
flag=true;
break;
}
}
if(flag) printf("NO\n");
else printf("YES\n");
}
return 0;
}
代码3:Trie树+数组实现1
非原创,代码来源:https://yuzhechuan.blog.luogu.org/solution-sp4033
时间复杂度:同代码1
#include <iostream>
#include <cstdio>
#include <cstring> //没打万能头是因为我先在poj交的,你懂的。。。
using namespace std;
int t,n,tot;
bool flag;
struct node{ //trie树节点
int go[10];
bool end;
}a[100005];
inline void ins(char *s){
if(flag) return ; //flag=1表示有符合条件的情况且已被找到所以可以尽管跳过
int x=0,l=strlen(s+1);
for(int i=1;i<=l;i++){ //遍历每个数字
int k=s[i]-'0';
if(!a[x].go[k]) a[x].go[k]=++tot;
else if(i==l) flag=1; //第一种情况判断:如果1个串的末尾在trie树中已被定义,则它定是前面某个串的前缀
x=a[x].go[k];
if(a[x].end) flag=1; //第二种情况判断:如果1个串中的某个字符是前面某个串结尾点,则前面定有一个串是它的前缀
}
a[x].end=1;//标记末尾节点
}
int main(){
scanf("%d",&t);
while(t--){
flag=tot=0;
memset(a,0,sizeof a); //全部清0
scanf("%d",&n);
for(int i=1;i<=n;i++){
char c[15];
scanf("%s",c+1); //我习惯从下标1开始存
ins(c); //插入trie树
}
if(flag) puts("NO"); //注意反着输
else puts("YES");
}
}
代码4:Trie树+数组实现2(更有技巧地使用数组)
非原创,代码来源:https://www.luogu.org/blog/Maroon5andLogan/solution-sp4033
时间复杂度:O(TNZ),N是结点个数,Z是字符集大小(10)。结点个数考虑极限情况,所有的字符串的字符都自成一个结点,每个字符串长度最大为10,所以结点N为n(10^4)*10,这里定义N=100005。
#include<bits/stdc++.h>
using namespace std;
#define RN register int
const int N=1e5+5;
const int Z=10;
int T,n,tot;
int ch[N][Z];//表示该节点的字符指针指向的节点,若为0,则无子节点
bool bo[N];
char s[20];
inline bool insert(char *s)//插入串,并判断.
{
int len=strlen(s);
int u=1;
bool flag=false;
for(RN i=0;i<len;i++)
{
int c=s[i]-'0';
if(!ch[u][c])//如果当前这个节点u没有指向c的指针,则建立一个
ch[u][c]=++tot;
else if(i==len-1)//如果搜完了,没有插入任何的新节点,则说明该串出现过,第1种情况
flag=true;
u=ch[u][c];
if(bo[u])//如果经过某个有标记的节点,属于第2种情况
flag=true;
}
bo[u]=true;
return flag;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
tot=1;//新建一个Trie树,其根节点为1
memset(ch,0,sizeof(ch));
memset(bo,false,sizeof(bo));//每次都要初始化
bool ans=false;
for(RN i=1;i<=n;i++)
{
scanf("%s",s);
if(insert(s))
ans=true;
}
if(!ans)//注意不要写反
puts("YES");
else
puts("NO");
}
return 0;
}
Luogu P2292 L语言
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n, m, dp[1100000]; //dp[x]=1说明前x个可以被理解
char word[11], text[1100000];
struct node{ int end; node* nxt[26]; node(){end=0; memset(nxt, 0, sizeof(nxt));}}*root;
void insert(char *s)
{
node *r=root; int l=strlen(s);
for(int i=0; i<l; i++)
{
int id=s[i]-'a';
if(r->nxt[id]==NULL) r->nxt[id]=new node();
r=r->nxt[id];
if(i==l-1) r->end=1;
}
}
void search(char *t, int pos) //从pos位置开始匹配
{
node *r=root;
while(t[pos]!='\0')
{
int id=t[pos]-'a';
if(r->nxt[id]==NULL) return;
r=r->nxt[id];
if(r->end) dp[pos]=1; //此位置有单词结束标记,标记pos位置待以后转移
pos++;
}
}
int main()
{
cin>>n>>m;
root=new node();
for(int i=1; i<=n; i++) cin>>word, insert(word);
for(int i=1; i<=m; i++)
{
cin>>(text+1);
memset(dp, 0, sizeof(dp)); //清空dp数组
dp[0]=1; //初始条件,前0个字符可匹配
int len=strlen(text+1);
for(int i=0; i<len; i++)
{
if(!dp[i]) continue;
search(text, i+1);
}
int ans;
for(ans=len; ans>0; ans--) if(dp[ans]) break; //倒序查找最大匹配位置
cout<<ans<<endl;
}
return 0;
}