hdu 3247 Resource Archiver(AC自动机+状压DP)

本文介绍了一种使用AC自动机结合状压动态规划解决字符串匹配问题的方法。通过构建AC自动机加速查找过程,并利用状压DP求解最优合并方案。

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

题意:给出m个病毒串,现在要把n个串合并,并且使得合并后的串中不包含病毒串。

思路:不会做只好看题解……首先把病毒串和要合并的串都插入ac自动机,并做上相应的标记。然后bfs求出每个要从串i到串j要走的距离dis[i][j],然后就是状压dp的部分了。用dp[i][j]表示状态到i(一个用二进制表示的已走的串),并且当前是以串j为结尾所需要走的最小步数。


代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
int ch[maxn][2],next[maxn],flag[maxn],sourcess[maxn],size;
void Insert(const char *s,int type)
{
    int u=0,n=strlen(s);
    for(int i=0;i<n;++i)
    {
        int c=s[i]-'0';
        if(!ch[u][c])
        {
            ch[u][c]=++size;
            memset(ch[size],0,sizeof(ch[size]));
            flag[size]=sourcess[size]=0;
        }
        u=ch[u][c];
    }
    if(type>=0) sourcess[u]=(1<<type);
    else flag[u]=1;
}
void build()
{
    queue<int>q;
    for(int c=0;c<2;++c)
        if(ch[0][c]) q.push(ch[0][c]);
    int r,u,v;
    while(!q.empty())
    {
        r=q.front();q.pop();
        for(int c=0;c<2;++c)
        {
            u=ch[r][c];
            if(!u) {ch[r][c]=ch[next[r]][c];continue;}
            q.push(u);
            v=next[r];
            if(v&&!ch[v][c]) v=next[v];
            next[u]=ch[v][c];
            sourcess[u]|=sourcess[next[u]];
            flag[u]|=flag[next[u]];
        }
    }
}
int dp[1<<10][20],dis[20][20],cnt,n,m;
int d[maxn],pos[20];
void Init()
{
    memset(ch[0],0,sizeof(ch[0]));
    memset(next,0,sizeof(next));
    flag[0]=sourcess[0]=size=0;
    cnt=1;pos[0]=0;
}
void bfs(int s)
{
    memset(d,0xff,sizeof(d));
    d[pos[s]]=0;
    queue<int>q;
    q.push(pos[s]);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int c=0;c<2;++c)
        {
            int v=ch[u][c];
            if(d[v]<0&&!flag[v])
            {
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
    for(int i=0;i<cnt;++i)
        dis[s][i]=d[pos[i]];
}
int solve()
{
    memset(dp,0xff,sizeof(dp));
    dp[0][0]=0;
    int total=1<<n;
    for(int i=0;i<total;++i)
    {
        for(int j=0;j<cnt;++j)
        {
            if(dp[i][j]<0) continue;
            for(int k=0;k<cnt;++k)
            {
                if(dis[j][k]<0) continue;
                if(dp[i|sourcess[pos[k]]][k]==-1)
                    dp[i|sourcess[pos[k]]][k]=dp[i][j]+dis[j][k];
                else
                    dp[i|sourcess[pos[k]]][k]=min(dp[i|sourcess[pos[k]]][k],dp[i][j]+dis[j][k]);
            }
        }
    }
    int ans=inf;
    for(int i=0;i<cnt;++i)
        if(dp[total-1][i]!=-1) ans=min(ans,dp[total-1][i]);
    return ans;
}
char str[maxn];
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        Init();
        for(int i=0;i<n;++i)
        {
            scanf("%s",str);
            Insert(str,i);
        }
        for(int i=0;i<m;++i)
        {
            scanf("%s",str);
            Insert(str,-1);
        }
        build();
        for(int i=1;i<=size;++i)
            if(sourcess[i]) pos[cnt++]=i;
        for(int i=0;i<cnt;++i)
            bfs(i);
        printf("%d\n",solve());
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值