hdu2296 Ring [kuangbin专题-ac自动机]

本文探讨了一种结合AC自动机与动态规划(DP)的算法解决策略,用于构造满足特定条件的字符串。重点介绍了如何通过AC自动机构造失败指针,以及如何在DP过程中保存最优字符串,确保输出的字符串不仅价值最大,而且长度最短,字典序最小。

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

比较常规的ac自动机结合dp的题,但这道题要求最后输出构造的字符串,我保存了一堆string。。总觉得不甚美观。
具体写法就是先插入字符串,构造fail指针,然后开始dp。dp[i][j]表示当前构造长度为i,所在的节点为j,能得到的最大的value。同时保存对应的字符串。注意需要字典序最小,所以得另外判一下。
需要注意的一点是题目的额外要求,如果存在多个value相同的串,优先长度最短,因此最后输出答案时我们得先找到value的最大值,同时保存最短的串的长度,之后只要找一下这个长度的值为最大值的字典序最小的串即可。

#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <queue>
#include <map>
#include <cstring>
#define fi first
#define se second
#define FIN freopen("in.txt","r",stdin)
#define FIO freopen("out.txt","w",stdout)
#define INF 0x3f3f3f3f
#define per(i,a,n) for(int i = a;i < n;i++)
#define rep(i,a,n) for(int i = n;i > a;i--)
#define pern(i,a,n) for(int i = a;i <= n;i++)
#define repn(i,a,n) for(int i = n;i >= a;i--)
#define fastio std::ios::sync_with_stdio(false)
#define all(a) a.begin(), a.end()
#define ll long long
#define pb push_back
#define endl "\n"
#define pii pair<int,int>
#define sc(n) scanf("%d", &n)
#define CASET int ___T; scanf("%d", &___T); for(int cs=1;cs<=___T;cs++)
template<typename T> inline void _max(T &a,const T b){if(a<b) a = b;}
template<typename T> inline void _min(T &a,const T b){if(a>b) a = b;}
using namespace std;
//inline ll read(){
//    ll a=0;int f=0;char p=getchar();
//    while(!isdigit(p)){f|=p=='-';p=getchar();}
//    while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();}
//    return f?-a:a;
//}

const int maxn = 5000;
const ll mod = 20090717;

const int maxnode = 26;



int ch[maxn][maxnode]; //字典树
int cnt[maxn];    //单词出现次数
int sz;
int fail[maxn];
map<char,int> mp;
int idx = 0;
int val[110];
void init()
{
    sz = 1;
    memset(ch[0], 0, sizeof(ch[0]));
    memset(cnt,0,sizeof(cnt));

    //val[0] = 0;
    cnt[0] = 0;
}
void insert(char str[], int len,int id) //插入字符串
{
    int u = 0;
    per(i, 0, len)
    {
        int v = str[i]-'a';
        if (!ch[u][v])
        {
            memset(ch[sz], 0, sizeof(ch[sz]));
            //val[sz] = 0;
            cnt[sz] = 0;
            ch[u][v] = sz++;
        }
        u = ch[u][v];
    }
    cnt[u]=id;
    //在这里我们可以建立一个int-string的映射,以通过节点序号得知这个点是哪个单词的结尾
}

void getfail()
{
    //所有模式串已插入完成
    queue<int> q;
    per(i, 0,maxnode)
    {
        if (ch[0][i])
        {
            fail[ch[0][i]] = 0;
            q.push(ch[0][i]);
        }
    }
    while (!q.empty())
    {
        int now = q.front();
        q.pop();
        per(i, 0,  maxnode)
        {
            if (ch[now][i])
            {
                fail[ch[now][i]] = ch[fail[now]][i];
                q.push(ch[now][i]);
            }
            else
                ch[now][i] = ch[fail[now]][i];
        }
        cnt[now] |= cnt[fail[now]];
    }
}

char s[100];
ll dp[55][maxn];
string path[55][maxn];
int main()
{

    #ifndef ONLINE_JUDGE
        int startTime = clock();
        FIN;
    #endif
    //fastio;
	//忘记初始化是小狗
    //freopen("out.txt","w",stdout);
    //ios::sync_with_stdio(false);
    int n,m;
    //cout << id('0');
    CASET
    {
        scanf("%d%d",&n,&m);
        //if(n==0)return 0;
        init();
        per(i,0,m)
        {
            scanf("%s",s);
            insert(s,strlen(s),i+1);
        }
        per(i,0,m)
        {
            scanf("%d",&val[i+1]);
        }
        getfail();
        memset(dp,-1,sizeof(dp));
        dp[0][0] = 0;
        per(i,0,n)
        {
            per(j,0,sz)
            {
                if(dp[i][j] == -1)continue;
                per(k,0,26)
                {
                    int v = ch[j][k];
                    if(dp[i][j]+val[cnt[v]]>dp[i+1][v])
                    {
                        dp[i+1][v] = dp[i][j]+val[cnt[v]];
                        path[i+1][v] = path[i][j]+(char)(k+'a');
                        //cout << path[i+1][v] << endl;
                    }
                    else if(dp[i][j]+val[cnt[v]]==dp[i+1][v])
                    {
                        string ss = path[i][j]+(char)(k+'a');
                        if(ss<path[i+1][v]) path[i+1][v] = ss;
                        //cout << path[i+1][v] << endl;
                    }
                }
            }
        }
        ll ans = 0,len = 0;
        string aa = "";
        pern(i,1,n)
        {
            per(j,0,sz)
            {
                if(dp[i][j]>ans)
                {
                    ans = dp[i][j];
                    len = i;
                }

            }
        }
        if(ans == 0)
        {
            cout <<endl;
            continue;
        }
        per(j,0,sz)
        {
            if(dp[len][j] == ans && (aa == "" || aa > path[len][j])) aa = path[len][j];
        }
        cout << aa << endl;
    }
    #ifndef ONLINE_JUDGE
        printf("\nTime = %dms\n", clock() - startTime);
    #endif
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值