hdu2296(AC自动机+DP)

本文介绍了一种结合AC自动机和动态规划的方法,用于解决如何构造一个特定长度下价值最大的字符串问题。通过构建Trie树,并利用DP记录路径上的最大价值及其对应的字符串路径,最终找到最优解。

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

题意:构造一个长度为n的字符串,价值最大。

思路:比较基础的AC自动机+DP,但是要求记录路径。dp[i][j]表示长度为i,在Trie图中的节点是j的最大价值,用path[i][j]记录路径。状态转移:dp[i][j] - > dp[i+1][从j出发能够到达的点] + val[j]。

代码如下:

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<stack>

#include<iostream>
#include<queue>
#include<string.h>

#define N 1500
using namespace std;

char s[105][15];
int dp[55][N];
string path[55][N];
struct Trie
{
    int sz;
    int ch[N][30],val[N],f[N],num[N];
    void init()
    {
        memset(ch,0,sizeof(ch));
        memset(f,0,sizeof(f));
        memset(val,0,sizeof(val));
        sz = 1;
    }
    void insert(char *s,int value)
    {
        int len = strlen(s);
        int u = 0;
        for(int i = 0; i < len; i++)
        {
            int c = s[i] - 'a';
            if(ch[u][c] == 0)   ch[u][c] = sz++;
            u = ch[u][c];
        }
        val[u] = value;
    }

    void getfail()
    {
        queue<int >q;
        int i,u = 0;
        for(i = 0; i < 26; i++)
            if(ch[u][i])    q.push(ch[u][i]);
        while(!q.empty())
        {
            u = q.front();
            q.pop();
            if(val[ f[u] ])
                val[u] += val[ f[u] ];
            for(i = 0; i < 26; i++)
            {
                int v = ch[u][i];
                if(v == 0)  ch[u][i] = ch[ f[u] ][i];
                else   {
                    q.push(v);
                    f[v] = ch[ f[u] ][i];
                }
            }
        }
    }
    void solve(int n)
    {
        int i,j,k;
        memset(dp,-1,sizeof(dp));

        dp[0][0] = 0;
        path[0][0] = "";
        for(i = 0; i < n; i++)
            for(j = 0; j < sz; j++)
                if(dp[i][j] != -1)
                    for(k = 0; k < 26; k++)
                    {
                        int u = ch[j][k];
                        if(dp[i][j] + val[u] > dp[i+1][u])
                        {
                            dp[i+1][u] = dp[i][j] + val[u];
                            path[i+1][u] = path[i][j]+(char)(k+'a');
                            continue;
                        }
                        if(dp[i][j] + val[u] == dp[i+1][u])
                        {
                            string str = path[i][j];
                            str.push_back(k+'a');
                            if(str < path[i+1][u])  path[i+1][u] = str;
                        }
                    }
        int ans = 0,row;
        string str;
        for(i = 0; i <= n; i++)
            for(j = 0; j < sz; j++)
                if(ans < dp[i][j]){
                    ans = dp[i][j];
                    row = i;
                }

        if(ans == 0)    {
            printf("\n");
            return;
        }
        str = "";
        for(j = 0; j < sz; j++)
            if(dp[row][j] == ans && ( str > path[row][j] || str == "") )
                str = path[row][j];
        cout<<str<<endl;
     }
}Trie;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        Trie.init();
        int n,m;
        scanf("%d%d",&n,&m);
        int i;
        for(i = 0; i < m; i++)
            scanf("%s",s[i]);
        for(i = 0; i < m; i++)
        {
            int x;
            scanf("%d",&x);
            Trie.insert(s[i],x);
        }
        Trie.getfail();
        Trie.solve(n);
    }
    return 0;
}

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值