UVAL 3942 - Remember the Word //Trie(模板) + dp

31 篇文章 0 订阅
6 篇文章 0 订阅

3942 - Remember the Word

题意:

给一个主串 S S S,给 n n n个串 T i T_i Ti,每个 T i T_i Ti可以用无数次,求用 T i T_i Ti组成 S S S的方法数。

思路:

显然可以用 d p dp dp递推求解,暴力递推肯定 T L E TLE TLE
考虑将 n n n T i T_i Ti做成字典树后,从后往前利用前缀转移
d p [ i ] = ∑ d p [ i + l e n ( x ) ] ∗ c n t [ x ] dp[i]=\sum dp[i+len(x)]*cnt[x] dp[i]=dp[i+len(x)]cnt[x] ( x x x为匹配的串, c n t [ x ] cnt[x] cnt[x] x x x的个数)
那么对于每一个 d p [ i ] dp[i] dp[i],每次从 T r i e Trie Trie树上走一遍即可
复杂度 O ( m a x ( l e n [ T i ] ) ∗ l e n [ s ] ) O(max(len[T_i])*len[s]) O(max(len[Ti])len[s])

/*   Author : Rshs
 *   Data : 2019-09-17-17.04
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 20071027;
const int MX = 1e6+5;  //字符最长长度
int trie[MX][28]; //i节点,j字符的编号
int col[MX];  //树上信息,(字符串个数)
int nonum;
void ins(char *x){ //插入
    int le=strlen(x),p=0;
    for(int i=0;i<le;i++){
        int c=x[i]-'a';
        if(!trie[p][c]){  //没的话新建
            trie[p][c]=++nonum;
        }
        p=trie[p][c];
    }
    col[p]++; //字符串x数量++
}
int sear(char *x){ //查找字符串x的数量
    int le=strlen(x),p=0;
    for(int i=0;i<le;i++){
        int c=x[i]-'a';
        if(!trie[p][c]) return 0;
        p=trie[p][c];
    }
    return col[p];
}
char s[300005],t[4005][105];int n;
LL dp[300005];
int main(){
    int cas=1;
    while(~scanf("%s",s)){
        memset(trie,0,sizeof(trie));
        memset(col,0,sizeof(col));
        int n;scanf("%d",&n);getchar();
        for(int i=1;i<=n;i++){
            scanf("%s",t[i]);
            ins(t[i]);
        }
        int le=strlen(s);
        dp[le]=1;
        for(int i=le-1;i>=0;i--){ //因为前缀建树,所以逆序求dp
            int p=0;dp[i]=0;
            for(int j=i;j<le;j++){
                int c=s[j]-'a';
                if(trie[p][c]==0) break;
                p=trie[p][c];
                dp[i]=(dp[i]+dp[j+1]*col[p]%mod)%mod;
            }
        }
        printf("Case %d: %lld\n",cas++,dp[0]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值