Resource Archiver hdu3247

博客详细探讨了Resource Archiver问题如何利用Trie图进行解决,并强调在处理生成串类问题时,由于存在禁止串,不能使用fail指针对策。文中提到了两种方法:一是仅通过字符转移,但可能忽略某些resource串的后缀关系;二是结合fail指针,转换后形成一个TSP问题。

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


通过这题加深了对trie图的认识,trie图建好之后,匹配就不再需要fail指针了,在解决生成串类问题中,由于有禁止的包含的串,一定不能用fail指针进行转移,否则生成的串中有可能包含禁止串。

 这道题关键在bfs求0点和resource串尾节点这些点俩俩之间最小距离,有俩种方法,

第一种是只通过ch来转移,但这样求出来的最短路不一定是最短的,因为有些resource串可能是另一些resource串的后缀,这相当于他们之间的最短路径是0,而这种情况是没有被考虑进来的,所以这就要求我们在DP转移时做文章,详细见代码。

第二种是通过ch和fail共同转移,但是通过fail指针进行转移时代价为0,而且只修改fail指针指向节点的距离值,但不把节点加入到队列中,这样处理完之后,就相当于一个普通的图了, 剩下的就是一个裸的TSP问题了。


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <cctype>
#include <utility>   
#include <map>
#include <string>  
#include <climits> 
#include <set>
#include <string>    
#include <sstream>
#include <utility>   
#include <ctime>

using std::priority_queue;
using std::vector;
using std::swap;
using std::stack;
using std::sort;
using std::max;
using std::min;
using std::pair;
using std::map;
using std::string;
using std::cin;
using std::cout;
using std::set;
using std::queue;
using std::string;
using std::istringstream;
using std::make_pair;
using std::getline;
using std::greater;
using std::endl;
using std::multimap;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PAIR;
typedef multimap<int, int> MMAP;

const int MAXN(60010);
const int SIGMA_SIZE(2);
const int MAXM(110);
const int MAXE(4000010);
const int MAXH(18);
const int INFI((INT_MAX-1) >> 2);
const int MOD(10007);
const ULL LIM(1000000000000000ull);

int point[12];

int que[MAXN];
int front, back;

struct AC
{
    int ch[MAXN][SIGMA_SIZE];
    int f[MAXN], state[MAXN];
    bool val[MAXN];
    int size;
    
    void init()
    {
        memset(ch[0], 0, sizeof(ch[0]));
        f[0] = state[0] = 0;
        val[0] = false;
        size = 1;
    }
    inline int idx(char temp)
    {
        return temp-'0';        
    }
    
    void insert(char *S, bool flag, int num = -1)
    {
        int u = 0, id;
        for(; *S; ++S)
        {
            id = idx(*S);
            if(!ch[u][id])
            {
                memset(ch[size], 0, sizeof(ch[size]));
                val[size] = false;
                state[size] = 0;
                ch[u][id] = size++;
            }
            u = ch[u][id];
        }
        if(!flag)
		{
            point[num] = u;
			state[u] |= (1 << num);  //标记resource串
		}
        val[u] |= flag;    //标记virus串
    }  
    void construct()
    {
        front = back = 0;
        int cur, u;
        for(int i = 0; i < SIGMA_SIZE; ++i)
        {
            u = ch[0][i];
            if(u)
            {
                que[back++] = u;
                f[u] = 0;
            }
        }
        while(front < back)
        {
            cur = que[front++];
            for(int i = 0; i < SIGMA_SIZE; ++i)
            {
                u = ch[cur][i];
                if(u)
                {
                    que[back++] = u;
                    f[u] = ch[f[cur]][i];
					state[u] |= state[f[u]];
                    val[u] |= val[f[u]];
                }
                else
                    ch[cur][i] = ch[f[cur]][i];
            }
        }
    }
};

AC ac;

void bfs(int s, int *dist)  //求0点和所有resource串结尾节点之间最短路径
{
    for(int i = 0; i < ac.size; ++i)
        dist[i] = INFI;
    dist[point[s]] = 0;
	front = back = 0;
	que[back++] = point[s];
	int cur, tv;
    while(front < back)
    {
       cur = que[front++];
	   tv = ac.ch[cur][0];   //注意此处只能通过ch进行转移
							 //如果通过fail指针转移则只能修改点权,但不能将其入队,如果将其入队,可能会导致
							 //访问路径中出现非法串
	   if(!ac.val[tv] && dist[tv] == INFI)
	   {
		   dist[tv] = dist[cur]+1;
		   que[back++] = tv;
	   }
	   tv = ac.ch[cur][1];
	   if(!ac.val[tv] && dist[tv] == INFI)
	   {
		   dist[tv] = dist[cur]+1;
		   que[back++] = tv;
	   }
    }
}

int dist[12][MAXN];
char str[1010];
int table[12][1 << 11]; 

void solve(int n)
{
    int lim = (1 << (n+1))-1;
    for(int i = 0; i <= n; ++i)
        for(int j = 0; j <= lim; ++j)
            table[i][j] = INFI;
    table[0][1] = 0;
    for(int i = 1; i < lim; ++i)
    {
        for(int j = 0; j <= n; ++j)
			if((i&(1 << j)) && table[j][i] != INFI)
				for(int k = 0; k <= n; ++k)
				{
					int ts = i|ac.state[point[k]];   //到达一个节点后,可能有多个串被同时访问
					table[k][ts] = min(table[k][ts], table[j][i]+dist[j][point[k]]);
				}
    }
    int ans = INFI;
    for(int i = 0; i <= n; ++i)
        ans = min(ans, table[i][lim]);
    printf("%d\n", ans);
}

int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m), n+m)
    {
        ac.init();
        point[0] = 0;
        for(int i = 1; i <= n; ++i)
        {
            scanf("%s", str);      //resource
            ac.insert(str, false, i);
        }
        for(int i = 0; i < m; ++i)
        {
            scanf("%s", str);      //virus
            ac.insert(str, true);
        }
        ac.construct();
        for(int i = 0; i <= n; ++i)
            bfs(i, dist[i]);
        solve(n);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值