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;
}
posted @ 2019-05-21 11:23  LFYZOI题解  阅读(238)  评论(0编辑  收藏  举报