[bzoj1030][JSOI2007]文本生成器——AC自动机+动态规划

题目大意:

给定n个给定的串,求有多少个串满足存在给定的串是这个串的子串。

思路:

直接计算存在的不太好算,考虑反面计数,计算有多少个串找不到匹配。
那么当我们把AC自动机给建出来之后,不难发现一个满足要求的串会一直在AC自动机上面跳并且到不了已经匹配上的节点,于是问题便转化成了有向图的路径计数。
考虑DP,记dp[i][j]表示走了i步走到了第j个节点,每一个点枚举下一个字符是什么,直接在AC自动机上转移即可。

#include<bits/stdc++.h>
 
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
 
using namespace std;
 
void File(){
    freopen("bzoj1030.in","r",stdin);
    freopen("bzoj1030.out","w",stdout);
}
 
template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}
 
const int maxn=6000+10;
const int mod=1e4+7;
int n,m,dp[110][maxn];
int ch[maxn][26],num[maxn],fail[maxn],cnt=1;
 
void insert(char *s){
    int len=strlen(s+1),u=1,c;
    REP(i,1,len){
        c=s[i]-'A';
        if(!ch[u][c])ch[u][c]=++cnt;
        u=ch[u][c];
    }
    ++num[u];
}
 
void build_fail(){
    int h=1,t=0,q[maxn];
    fail[1]=1;
    REP(i,0,25){
        int v=ch[1][i];
        if(v)q[++t]=v;
        if(v)fail[v]=1;
        else ch[1][i]=1;
    }
    while(h<=t){
        int u=q[h++];
        REP(i,0,25){
            int v=ch[u][i];
            if(v)q[++t]=v;
            if(v){
                fail[v]=max(ch[fail[u]][i],1);
                num[v]|=num[fail[v]];
            }
            else ch[u][i]=max(ch[fail[u]][i],1);
        }
    }
}
 
void ad(int &x,int y){x=(x+y)%mod;}
 
void work(){
    dp[1][1]=1;
    REP(i,1,m)REP(j,1,cnt)REP(k,0,25)
        if(!num[ch[j][k]])
            ad(dp[i+1][ch[j][k]],dp[i][j]);
    int ans=1;
    REP(i,1,m)ans=ans*26%mod;
    REP(i,1,cnt)ad(ans,-dp[m+1][i]);
    printf("%d\n",(ans+mod)%mod);
}
 
int main(){
    //File();
    char s[maxn];
 
    read(n); read(m);
    REP(i,1,n){
        scanf("%s",s+1);
        insert(s);
    }
 
    build_fail();
 
    work();
    //cout<<"÷"<<endl;
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用[1],dp[u][j]表示在u子树中选取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果选择节点u,则dp[u][j] = dp[v][j-1] + value[u],其中v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不选择节点u,则dp[u][j] = max(dp[v][j]),其中v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所求的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的数据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划求解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 求解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值