AGC047 B - First Second(字典树)

题意:

对于一个字符串S,一次操作你可以将左边第一个或者第二个字符删除,可以进行无限次操作。

给定n个字符串S(1),S(2)…S(n),
问有多少个有序数对(i,j),S(i)和S(j)其中一个串通过操作可以得到另外一个。

数据范围:n<=2e5, ∑ \sum |S(i)|<=1e6,所有串互不相同且只由[a,z]组成

解法:

假设串的下标从1开始,
串S通过操作能获得的字符串一定是S的某个真后缀suf[pos],左边再加上[1,pos-1]中的某个字符。

先将所有串倒着插入字典树,
对于每个串T,找能得到它的串S:
T[2,n]一定是S的真后缀S[pos,m],同时S[1,pos-1]还出现过T[1],
令d(x,k)表示字典树以x为根的子树中,还出现过k的字符串结尾个数,可以用树形dp预处理,
那么统计答案就很简单了。
(预处理的细节还蛮多的)

最后要减掉n,因为每个串会匹配到自己。

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PI pair<int,int>
const int maxm=1e6+5;
string s[maxm];
int ans;
int n;
struct Trie{
    int a[maxm][26],tot;
    int type[maxm];//节点x的字符类型
    int val[maxm];//节点x为根的子树endpos个数
    int ed[maxm][26];//ed[i][j]表示i为根的子树中经过j的endpos数
    void add(string s){
        int len=s.size();
        int node=0;
        for(int i=len-1;i>=0;i--){
            int v=s[i]-'a';
            if(!a[node][v]){
                a[node][v]=++tot;
                type[tot]=v;
            }
            node=a[node][v];
        }
        val[node]++;
    }
    void dfs(int x){
        for(int i=0;i<26;i++){
            int v=a[x][i];
            if(!v)continue;
            dfs(v);
            val[x]+=val[v];
            for(int j=0;j<26;j++){
                ed[x][j]+=ed[v][j];
            }
        }
        ed[x][type[x]]=val[x];//
    }
    void cal(string s){
        int len=s.size();
        int node=0;
        for(int i=len-1;i>=1;i--){
            int v=s[i]-'a';
            node=a[node][v];
        }
        for(int i=0;i<26;i++){
            int v=a[node][i];
            if(!v)continue;
            ans+=ed[v][s[0]-'a'];
        }
    }
}T;
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>s[i];
        T.add(s[i]);
    }
    T.dfs(0);
    for(int i=1;i<=n;i++){
        T.cal(s[i]);
    }
    ans-=n;
    cout<<ans<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值