UVALive - 3942(前缀树DP)

给出一个字符串和一些字符片段,求由这些字符片段组成这个字符串可以有多少种组的方法。


Sample Input
abcd
4
a
b
cd
ab
Sample Output
Case 1: 2

明显 对于字符串上的每个位置而言,如果用dp【i】代表从i开始的后缀最多有多少种组合方式的话,dp【i】 = sum(dp【i】 + len(x)| 这里的x代表从i开始的后缀的第一个组成部分的长度);

如果直接枚举的话最坏的情况要 每次匹配4000种 每次匹配的过程中又要花费时间 .

直接在树上查找的话最多100次就可以判断所有的情况。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>

using namespace std;
const int maxn = 300000+10;
const int mod = 20071027;
char s[maxn],s1[maxn];
int ch[maxn][30];
int val[maxn];
int dp[maxn];

int idx( char ch ) {return ch-'a';} // 字母的编号

struct trie
{
    int sz;
    trie(){sz=1;memset(ch[0],0,sizeof(ch[0]));}

    void Insert(char *s , int v) //将字符段插入树中
    {
        int u = 0, n = strlen(s);

        for(int i=0;i<n;i++)
        {
            int c = idx(s[i]);
            if(!ch[u][c])// 如果当前的节点下没有这个字母
            {
                memset(ch[sz],0,sizeof(ch[sz]));
                ch[u][c] = sz++;
            }
            u = ch[u][c];// 向下
        }
        val[u] = v; // 在一个字符段结束时打上标记
    }
    int Find(char *s,int pos)
    {
        int u = 0, n = strlen(s);
        int ans = 0;
        for(int i=pos;i<n&&i-pos<=100;i++) // 在后缀中查找能出现的所有的字符段
        {
            int c = idx(s[i]);

            if(!ch[u][c]) return ans;

            u = ch[u][c];

            if(val[u]) // 是一个字符段的结束
            {
                ans = (ans + dp[i+1]) % mod;
            }
        }
        return ans;
    }

};
int m;
int main()
{
    int kase = 1;
    while(scanf("%s",s)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        memset(ch,0,sizeof(ch));
        memset(val,0,sizeof(val));
//        cout << s  <<endl;
        trie tree = trie();
//        getchar();
        cin >> m;
        for(int i=0;i<m;i++)
        {
            scanf("%s",s1);
//            cout << s1 << endl;
            tree.Insert(s1,1);
        }
        int n = strlen(s);
        dp[n] = 1;
        for(int i=n-1;i>=0;i--)
        {
            dp[i] = tree.Find(s,i);
        }
        printf("Case %d: ",kase++);
        cout << dp[0] << endl;
    }
    return 0;
}

 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值