【HDU 1247】字典树 一个单词由两个单词组成

本文通过实战案例详细解析了字典树(Trie)的构建与应用,包括节点结构定义、插入与查找算法,并分享了如何高效处理字符串匹配问题的经验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
#define MAX 26
struct node;
typedef struct node *NumTree;

struct node
{
    int tag;
    NumTree ans[MAX];
};

NumTree init(NumTree T)
{
    int i;
    T=new struct node;
    for(i=0;i<MAX;i++)
    {
        T->tag=0;
        T->ans[i]=NULL;
    }
    return T;
}

void insert(NumTree T,char str[])
{
    NumTree Q=T,S=NULL;
    int len=strlen(str);
    int i,id;
    for(i=0;i<len;i++)
    {
        id=str[i]-'a';
        if(Q->ans[id]==NULL)
        {
            S=new struct node;
            int j;
            for(j=0;j<MAX;j++)
            {
                S->ans[j]=NULL;
                S->tag=0;
            }
            Q->ans[id]=S;
            Q=Q->ans[id];
        }
        else
        {
            Q=Q->ans[id];
        }
    }
    Q->tag=1;
}

int find(NumTree T,char str[])
{
    NumTree Q=T;
    int len=strlen(str);
    int i,id;
    queue<int>Stack;
    for(i=0;i<len;i++)
    {
        id=str[i]-'a';
        if(Q->ans[id]!=NULL)
        {
            Q=Q->ans[id];
        }
        if(Q->tag && i<len)//find sepraste point
        {
            Stack.push(i);
        }
    }
    while(!Stack.empty())
    {
        int now=Stack.front();
        now++;
        bool OK=true;
        Stack.pop();
        Q=T;
        while(str[now])
        {
            id=str[now++]-'a';
            if(Q->ans[id]==NULL)
            {
                OK=false;
                break;
            }
            Q=Q->ans[id];
        }
        if(OK && Q->tag)
            return 1;
    }
    return 0;
}

int main()
{
    char str[50011][20];
    NumTree T=NULL;
    int x=0;
    T=init(T);
    while(gets(str[x]) && str[x][0]!='\0')
    {
        insert(T,str[x]);
        x++;
    }
    int u;
    for(u=0;u<x;u++)
    {
        if(find(T,str[u])==1)
            cout<<str[u]<<endl;
    }
    return 0;
}



搞了一下午····

不得不说还是有一些收获,RE的次数最多,

这里要说一下,RE分两种,一种越界,一种是栈溢出,

这里在定义str的时候,

如果定义str[50001][100],就会栈溢出,可见字典树对字符长度的要求很高,不能太长。


整个代码的思路就是在insert同时的时候标记,一个字符如果是一个字符串的结尾字符,那么标记,

在find的时候,如果找到了一个标记,找到了A个标记,说明这个字符串里含有A个字符串,但无法确定这A个字符串是否是紧邻的,这里需要用STACK或者QUEUE,最好还是用STACK,依次记录有标记的下标,然后从后往前开始查询,


这里还有一个小细节,在查询的时候now++,因为top回来的now是一个字符的结尾,重新遍历的时候应该是从找到标记的字符的后一个字符开始。


然后如果有两个以上标记是怎么回事呢?


如果


abcdefg

1001001

有三个标记,这时候从最后一个1开始找,然后从倒数第二个1找,而这里的1代表,至少字典里有a,abcd,abcdefg三个字符串(也可能有defg,bcd等等),也就是说我从标记的下一个字符开始找起,标记的前半部分已经肯定在字符串里了(也就是组成该字符串的前半段),只需要证明从标记开始的字符串也在字典树里,那么就满足该字符串由两个字符串构成,输出即可。



这题还有一个思路,就是把每一个字符串在不同位置“砍一刀”,变成两个字符串,然后find(str1)find(str2),如果都能找到,那么就说明它(这个字符串)有两个字符串构成,这个代码我也实现了,但是不知道为什么一直RE,可能是迭代或者递归的层数太多了(find要递归,如果字符串很长的话,也要砍很多刀)。暂时我还没有想到比较好的优化方案。砍刀也许可以用二分查找实现,但那仍然依赖输入。对效率提升不一定十分明显。


#include <iostream>
#include <string.h>
using namespace std;
#define MAX 26
struct node;
typedef struct node *NumTree;

struct node
{
    NumTree ans[MAX];
};

NumTree init(NumTree T)
{
    int i;
    T=new struct node;
    for(i=0;i<MAX;i++)
        T->ans[i]=NULL;
    return T;
}

void insert(NumTree T,char str[])
{
    NumTree Q=T,S=NULL;
    int len=strlen(str);
    int i,id;
    for(i=0;i<len;i++)
    {
        id=str[i]-'a';
        if(Q->ans[id]==NULL)
        {
            S=new struct node;
            int j;
            for(j=0;j<MAX;j++)
                S->ans[j]=NULL;
            Q->ans[id]=S;
            Q=Q->ans[id];
        }
        else
        {
            Q=Q->ans[id];
        }
    }
}

int find(NumTree T,char str[])
{
    NumTree Q=T;
    int len=strlen(str);
    int i,id;
    int tag=1;
    for(i=0;i<len;i++)
    {
        id=str[i]-'a';
        if(Q->ans[id]!=NULL)
            Q=Q->ans[id];
        else
        {
            tag=0;
            break;
        }
    }
    return tag;
}

void FreeTree(NumTree T)
{
    int i;
    for(i=0;i<MAX;i++)
    {
        if(T->ans[i]!=NULL)
            FreeTree(T->ans[i]);
    }
    free(T);
}


int main()
{
    char str[50011][100];
    NumTree T=NULL;
    int x=0;
    T=init(T);
    while(gets(str[x]) && str[x][0]!='\0')
    {
        insert(T,str[x]);
        x++;
    }
    int u;
    for(u=0;u<x;u++)
    {
        int i,j,k=1;
        int len=strlen(str[u]);
        char str1[100],str2[100];
        while(k<len)
        {
            for(i=0;i<k;i++)
                str1[i]=str[u][i];
            str1[i]='\0';
            for(j=0;i<len;i++,j++)
                str2[j]=str[u][i];
            str2[j]='\0';
            if(find(T,str1) && find(T,str2))
            {
                printf("%s\n",str[u]);
                break;
            }
            k++;
        }
    }
    //FreeTree(T);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值