uva1519 - Dictionary Size

博客围绕https://vjudge.net/problem/UVA - 1519题目展开。题解指出该题与后缀数组无关,推测是字典树或AC自动机题。思路是将所有串建成字典树,枚举前缀并连接后缀,减去重复统计的串,还可反转串建字典树统计字母出现次数。最后提及有对应代码。

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

链接

https://vjudge.net/problem/UVA-1519

题解

首先这题很显然和后缀数组扯不上关系,暴力又想不出来,所以感觉就是字典树或者AC自动机题
考虑把所有串建成字典树
那么字典树上每一个结点代表一个前缀,遍历字典树就可以做到不重复不遗漏地枚举所有前缀
现在考虑在每个前缀上连接后缀
首先我先连接上所有的后缀,然后考虑减去那些重复统计的
如果一个后缀,它在单词种的前一个字母和当前前缀的最后一个字母相同,那么在当前前缀所代表结点的父亲节点就已经统计过这个串,所以应当减去这样的串
把所有串反转建字典树,然后统计每个字母的出现次数即可解决这个问题

代码

#include <bits/stdc++.h>
#define maxn 400010
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
struct Trie
{
    int trie[maxn][27], tot, fail[maxn], tail[maxn], alpha[maxn], depth[maxn];
    void clear()     //clear the arrays
    {
        for(int i=1;i<=tot;i++)cl(trie[i]), fail[i]=tail[i]=depth[i];
        tot=1;
    }
    int insert(int *r, int len)     //insert a string into trie tree
    {
        auto pos=1;
        for(auto i=1;i<=len;i++)
            pos = trie[pos][r[i]] ? trie[pos][r[i]] : trie[pos][r[i]]=++tot;
        tail[pos]++;
        return pos;
    }
    void dfs(int pos)
    {
        for(auto i=1;i<=26;i++)
        {
            if(trie[pos][i])
            {
                auto v(trie[pos][i]);
                depth[v]=depth[pos]+1;
                alpha[v]=i;
                dfs(v);
            }
        }
    }
}pre, suf;
char s[45];
int r[45], cnt[30], vis[30];
int main()
{
    freopen("in.txt","r",stdin);
    int n, len, i, j;
    ll ans;
    while(~scanf("%d",&n))
    {
        pre.clear();
        suf.clear();
        cl(cnt);
        cl(vis);
        ans=0;
        for(i=1;i<=n;i++)
        {
            scanf("%s",s+1);
            len=strlen(s+1);
            if(len==1)
            {
                vis[s[1]-'a'+1]++;
                if(vis[s[1]-'a'+1]==1)ans++;
            }
            for(j=1;j<=len;j++)r[j]=s[j]-'a'+1;
            pre.insert(r,len);
            reverse(r+1,r+len+1);
            suf.insert(r,len);
        }
        pre.dfs(1);
        suf.dfs(1);
        for(auto i=1;i<=suf.tot;i++)
        {
            if(suf.depth[i]>1)cnt[suf.alpha[i]]++;
        }
        for(auto i=2;i<=pre.tot;i++)
        {
            ans+=suf.tot-1;
            if(pre.depth[i]>1)ans-=cnt[pre.alpha[i]];
        }
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值