hdu3341 Lost‘s revenge [kuangbin专题-ac自动机]

本文探讨了一种针对仅含ACTG字符的字符串匹配问题的优化算法。通过使用字典树和动态规划,解决了在给定长度限制下,如何构造包含输入字符串最多的字符串,同时每个字符出现次数受限的问题。算法采用状态压缩,利用一维数组存储四种字符的出现次数,通过进位方式高效表示状态。

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

这题也很有意思,不只是单纯的要构造出含输入字符串最多的字符串,单个字符还有数量上限。题目的说法是对原字符串进行重新安排,但具体怎么安排,以及操作次数这些是没说的,所以我们可以理解成直接重新构造字符串,并且每个字符出现的次数不能超过原字符串中它出现的次数。
这道题中的字符只含ACTG,简化了问题。最终构造出的字符串长度小于40,但如果对ACTG每个字符都开40的大小,那需要404040*40的空间,显然不可能,但我们注意到这四个字符加起来最多也只能出现40次,所以考虑状压,用一维保存这四个字符出现的次数。
考虑ACTG出现多少次,才能有最多的状态?不难发现,各取10个是状态最多的情况。因此状态数取114,然后ACTG出现的次数我们可以用类似进位的方式从一个数中提取出来。这道题一共50个串,单个长度不超过十,所以字典树节点可以开500多点,然后dp数组第二维存ACTG出现的次数的状态,大小开114,下面就可以进行dp了。
dp前我们还需要先把进位做一下,我们让A当第一维,那么一个A指代1,A出现的次数就是表示状态的数的最后一位的进制;C当第二维,一个C指代的值等于A出现的次数,倒数第二位的进制就是A出现的次数乘以C出现的次数,依此推出T与G。然后状态就可以表示成当前各个字符出现的次数,分别乘以他们指代的值,终态就是原串中各个字符出现的次数,也就是出现次数的最大值,与他们指代的值的乘积,我们在这个状态上找最大值即可。

#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 = 550;

const int maxnode = 4;



int ch[maxn][maxnode]; //字典树
int cnt[maxn];    //单词出现次数
int sz;
int fail[maxn];
map<char,int> ma;
char s[50];
int n;
int num[4];
void init()
{
    num[0] = num[1] = num[2] = num[3] = 0;
    ma['A'] = 0;
    ma['C'] = 1;
    ma['G'] = 2;
    ma['T'] = 3;
    sz = 1;
    memset(ch[0], 0, sizeof(ch[0]));
    memset(cnt,0,sizeof(cnt));
    cnt[0] = 0;
}
void insert(char str[], int len) //插入字符串
{
    int u = 0;
    per(i, 0, len)
    {
        int v = ma[str[i]];
        if (!ch[u][v])
        {
            memset(ch[sz], 0, sizeof(ch[sz]));
            cnt[sz] = 0;
            ch[u][v] = sz++;
        }
        u = ch[u][v];
    }
    cnt[u]++;
    //在这里我们可以建立一个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]];
    }
}
int b[4];
int dp[550][11*11*11*11+100];
int main()
{

    #ifndef ONLINE_JUDGE
        int startTime = clock();
        FIN;
    #endif
    //fastio;
	//忘记初始化是小狗
    //freopen("out.txt","w",stdout);
    //ios::sync_with_stdio(false);
    int _T = 0;
    while(~sc(n))
    {
        if(n == 0)return 0;
        init();
        memset(dp,-1,sizeof(dp));
        per(i,0,n)
        {
            scanf("%s",s);
            insert(s,strlen(s));
        }
        getfail();
        scanf("%s",s);
        int len = strlen(s);
        per(i,0,len)
        {
            num[ma[s[i]]]++;
        }
        //per(i,0,4)cout << num[i] << ' ';
        b[0] = 1;
        b[1] = num[0]+1;
        b[2] = b[1]*(num[1]+1);
        b[3] = b[2]*(num[2]+1);
        dp[0][0] = 0;
        pern(A,0,num[0])
        {
            pern(C,0,num[1])
            {
                pern(G,0,num[2])
                {
                    pern(T,0,num[3])
                    {
                        int st = A+C*b[1]+G*b[2]+T*b[3];
                        per(j,0,sz)
                        {
                            if(dp[j][st] == -1)continue;
                            per(k,0,4)
                            {
                                if(k == 0 && A == num[0])continue;
                                if(k == 1 && C == num[1])continue;
                                if(k == 2 && G == num[2])continue;
                                if(k == 3 && T == num[3])continue;
                                int v = ch[j][k];
                                dp[v][st+b[k]] = max(dp[v][st+b[k]],dp[j][st]+cnt[v]);
                            }
                        }
                    }
                }
            }
        }
        int ans = 0;
        int st = num[0]+num[1]*b[1]+num[2]*b[2]+num[3]*b[3];
        per(i,0,sz)ans = max(ans,dp[i][st]);
        printf("Case %d: %d\n",++_T,ans);
    }
    #ifndef ONLINE_JUDGE
        printf("\nTime = %dms\n", clock() - startTime);
    #endif
    return 0;
}
HDU 2034 是一道经典的 A-B Problem 题目,通常涉及简单的数学运算或者字符串处理逻辑。以下是对此类问题的分析以及可能的解决方法。 ### HDU 2034 的题目概述 该题目要求计算两个数之间的差值 \(A - B\) 并输出结果。需要注意的是,输入数据可能存在多种情况,因此程序需要能够适应不同的边界条件和特殊情况[^1]。 #### 输入描述 - 多组测试数据。 - 每组测试数据包含两行,分别表示整数 \(A\) 和 \(B\)。 #### 输出描述 对于每组测试数据,输出一行表示 \(A - B\) 的结果。 --- ### 解决方案 此类问题的核心在于正确读取多组输入并执行减法操作。以下是实现此功能的一种常见方式: ```python while True: try: a = int(input()) b = int(input()) print(a - b) except EOFError: break ``` 上述代码片段通过循环不断接收输入直到遇到文件结束符 (EOF),适用于批量处理多组测试数据的情况。 --- ### 特殊考虑事项 尽管基本思路简单明了,在实际编码过程中仍需注意以下几点: 1. **大数值支持**:如果题目中的 \(A\) 或 \(B\) 可能非常大,则应选用可以容纳高精度的数据类型来存储这些变量。 2. **负数处理**:当 \(B>A\) 导致结果为负时,确保程序不会因符号错误而失效。 3. **异常捕获**:为了防止运行期间由于非法字符或其他意外状况引发崩溃,建议加入必要的错误检测机制。 --- ### 示例解释 假设给定如下样例输入: ``` 5 3 7 2 ``` 按照以上算法流程依次完成各步操作后得到的结果应当分别为 `2` 和 `5`。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值