HDU4758 AC自动机+DP (HDU4758与HDU2222)

通过使用滚动数组优化动态规划(DP)算法,显著提高了HDU4758题目的解决效率。该方法通过减少状态数组的维度,实现了更高效的内存管理和更快的运行速度。

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

竟然刚刚发现滚动数组可以提高DP 的效率(旁白:这是为什么呢。。。)

总之DP 的题目实现效率非常重要啊ORZ

HDU4758 求一个含有固定数量的0和固定数量的1 的01串,使得其包含两个给定的子串(子串不会相同)

最终的解决方案是用两个子串建立一个AC自动机,然后利用:

A) dp[L][n1][k][s] 其中L 表示当前dp状态串的总长,n1表示1的个数,k表示当前状态在AC自动机中的位置,s是一个二进制状压的量,表示有几个子串被满足;

B) dp[n0][n1][k][s] n0表示当前0的个数 后面的同上。


一般地说B) 是比较直观的想法,不过A) 的好处是可以使用一个滚动数组把第一个维度的大小缩小为2,而这个滚动可以显著地提高效率。。。(旁白这究竟是为什么呢。。。)

另外一个优化是形如:

if(dp[i][j][k][e]==0) continue;
加了这个优化之后想法B) 可以卡线过时限。


还有一个需要注意的地方是在构造失配指针的时候要增加一个形如:

tree[p].val |= tree[tree[p].fal].val;

的转移,因为在实现AC自动机+DP 的时候不能够像正常的AC自动机(形如HDU2222) 一样递归寻找匹配,所以要把所有p节点的失配节点的匹配统一到节点p上去。

总之。。。 TLE 了很多次 m(-___-)m


#include<cstdio>
#include<cstring>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;

const int Maxm = 200;
const int Maxn = 100;
const int An = 2;
const int inf = 1000000007;
const char Char0 = 'a';

int tn;

struct node{
    int fal,val;
    int s[An+1];
    void init(){
        fal=val=0;
        memset(s,0,sizeof(s));
    }
}tree[Maxm+10];

void init_tree(){
    tree[0].init();
    tn=1;
}

void insert_word(int id,char *str){
    int lb=0;
    while(*str){
        if(!tree[lb].s[*str-Char0]){
            tree[tn].init();
            tree[lb].s[*str-Char0]=tn++;
        }
        lb=tree[lb].s[*str-Char0];
        ++str;
    }
    tree[lb].val |= id;
}

void build_tree(){
    queue<int> q;
    while(!q.empty()) q.pop();
    q.push(0);
    while(!q.empty()){
        int p=q.front();
        if(tree[p].fal==p) tree[p].fal=0;
        for(int i=0;i<An;i++)
            if(tree[p].s[i]){
                tree[tree[p].s[i]].fal=tree[tree[p].fal].s[i];
                tree[tree[p].s[i]].val |= tree[tree[tree[p].s[i]].fal].val;
                q.push(tree[p].s[i]);
            }else tree[p].s[i]=tree[tree[p].fal].s[i];
        q.pop();
    }
}

int dp[2][Maxn+10][Maxm+10][4]; // X Y Automaton_ID Matching
int t,n,m;
char s[200];

int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        gets(s);
        init_tree();
        gets(s);
        for(int i=0;i<strlen(s);i++)
            if(s[i]=='R') s[i]='a';
            else s[i]='b';
        insert_word(1,s);
/*
        for(int i=0;i<strlen(s);i++)
            printf("%c",s[i]);
        printf("\n");
*/
        gets(s);
        for(int i=0;i<strlen(s);i++)
            if(s[i]=='R') s[i]='a';
            else s[i]='b';
        insert_word(2,s);
/*
        for(int i=0;i<strlen(s);i++)
            printf("%c",s[i]);
        printf("\n");
*/
        build_tree();
        memset(dp,0,sizeof(dp));
        dp[0][0][0][0]=1;
        for(int i=0;i<=n+m;i++){
            memset(dp[(i+1)&1],0,sizeof(dp[(i+1)&1]));
            for(int j=0;j<=m;j++)
                for(int k=0;k<tn;k++)
                for(int e=0;e<4;e++){
                    dp[(i+1)&1][j][tree[k].s[0]][e | tree[tree[k].s[0]].val]+=dp[i&1][j][k][e];
                    dp[(i+1)&1][j][tree[k].s[0]][e | tree[tree[k].s[0]].val]%=inf;
                    if(j==m) continue;
                    dp[(i+1)&1][j+1][tree[k].s[1]][e | tree[tree[k].s[1]].val]+=dp[i&1][j][k][e];
                    dp[(i+1)&1][j+1][tree[k].s[1]][e | tree[tree[k].s[1]].val]%=inf;
                }
        }
        int ans=0;
        for(int i=0;i<tn;i++){
            ans+=dp[(n+m)&1][m][i][3];
            ans%=inf;
        }
        printf("%d\n",ans);
    }
    return 0;
}

这里发的是滚动数组的版本


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值