HDOJ 2846 Repository (字典树变形)

本文介绍了一种使用字典树解决多个源字符串中单词匹配问题的方法,通过插入后缀单词并携带身份标记,避免了重复计数,实现高效匹配。

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

点击打开链接


这道题的题意就是计算输入的单词被包含在几个源字符串中。

因为字典树是与前缀有关的,所以将每个源字符串的后缀都插入到字典树中去。这样在查找字典树是相当于从源字符串的中间位置开始查找。

有一个关键的问题就是如何判断两个单词是不是来自与同一个源字符串呢?比如源字符串add d既包含于dd又包含于d。但只能算一次。

这就要求我们在插入后缀单词的时候,带上一个身份标记表明来自哪一个编号的源字符串,如果在插入过程中,经过节点的时候,和节点上的标记num不同,那么更新num为这个新插入的单词的身份标记,并且累加这个节点的val。

上面这种操作是为了不重复累加val而服务的。为什么这么做就可以呢?因为来自同一个源字符串的所有后缀单词一定是连续地插入的。自己再体会下。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<string>
#define cg(x) x - 'a';
using namespace std;
int sum = 0, ans = 0;
const int MAXN = 1e6;
struct trie{
int next[26];
int val;
int num;
}tree[MAXN];
void insert(string s, int t)
{
    int root = 0;
    int l = s.size();
    for (int i = 0; i < l; i++)
    {
        int c = cg(s[i]);
        if (tree[root].next[c]) root = tree[root].next[c];
        else
        {
            sum++;
            tree[root].next[c] = sum;
            root = sum;
            memset(&tree[root], 0, sizeof(trie));
        }
        if (tree[root].num != t)
        {
            tree[root].num = t;
            tree[root].val++;
        }
    }
}
void find(string s)
{
    int root = 0, flag = 0;
    int l = s.size();
    for (int i = 0; i < l; i++)
    {
        int c = cg(s[i]);
        if (tree[root].next[c]) root = tree[root].next[c];
        else {flag = 1; break;}
    }
    if (flag == 0) ans += tree[root].val;
}
int main()
{
    int n, l;
    string s, s2;
    cin >> n;
    memset(&tree[0], 0, sizeof(trie));
    for (int i = 1; i <= n; i++)
    {
        cin >> s;
        l = s.size();
        for (int j = 0; j < l; j++)
        {
            s2 = s.substr(j, l - j);
            insert(s2, i);
        }
    }
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        ans = 0;
        cin >> s;
        find(s);
        cout << ans << endl;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值