[USACO12JAN][SPOJ10502][Luogu3041]Video Game Combos

传送门
SPOJ10502
Luogu3041

Description

Bessie is playing a video game! In the game, the three letters ‘A’, ‘B’,and ‘C’ are the only valid buttons. Bessie may press the buttons in any order she likes; however, there are only N distinct combos possible (1 <= N<= 20). Combo i is represented as a string S_i which has a length between 1 and 15 and contains only the letters ‘A’, ‘B’, and ‘C’.
Whenever Bessie presses a combination of letters that matches with a combo, she gets one point for the combo. Combos may overlap with each other or even finish at the same time! For example if N = 3 and the three possible combos are “ABA”, “CB”, and “ABACB”, and Bessie presses “ABACB”, she will end with 3 points. Bessie may score points for a single combo more than once.
Bessie of course wants to earn points as quickly as possible. If she presses exactly K buttons (1 <= K <= 1,000), what is the maximum number of points she can earn?
给你个模式串(每个长度≤15,1≤N≤20),串中只含有“ABC”三种字母。求一长度为K(1≤K≤1000)的字符串,使得匹配数最大(重复匹配计多次),输出最大值。

Input

  • Line 1: Two space-separated integers: N and K.
  • Lines 2..N+1: Line i+1 contains only the string S_i, representing combo i.

Output

  • Line 1: A single integer, the maximum number of points Bessie can obtain.

Sample Input

3 7
ABA
CB
ABACB

Sample Output

4

Hint

The optimal sequence of buttons in this case is ABACBCB, which gives 4 points–1 from ABA, 1 from ABACB, and 2 from CB.

Solution

  • AC自动机模板+DP
  • val[u]表示后缀树上u及其子孙中危险节点总和。(此处后缀树指fail指针反向构成的树)
  • f[step][u]表示第step步走到u节点的最优答案
  • 需要注意的是如果当前危险节点的模式串长度小于step,答案是不可以更新的,p数组用来判断这个,相当于表示该状态有没有到达过。第一次由于这个WA掉了

Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
queue <int>q;
char s[27];
int n,m,sz;
int f[1007][307],ch[307][7],val[307],fail[307];
bool p[1007][307];
void insert(char *s,int id){
    int u=1,n=strlen(s);
    for (int i=0;i<n;i++){
        int c=s[i]-'A';
        if (!ch[u][c]){
            //memset(ch[sz],0,sizeof(ch[sz]));
            //val[sz]=0;
            ch[u][c]=++sz;
        }
        u=ch[u][c];
    }
    val[u]=1;
}
void getFail(){
    int rt=1;
    fail[rt]=1;
    for (int i=0;i<3;i++){
        int u=ch[rt][i];
        fail[u]=rt;
        if (u){fail[u]=rt; q.push(u);}
            else ch[rt][i]=rt;
    }
    while (!q.empty()){
        int u=q.front(); q.pop();
        for (int i=0;i<3;i++){
            int v=ch[u][i];
            if (v){fail[v]=ch[fail[u]][i]; q.push(v);}
                else ch[u][i]=ch[fail[u]][i];
        }
        if (val[fail[u]]) val[u]+=val[fail[u]];
    }
}
int main(){
    scanf("%d%d",&n,&m);
    sz=1;
    for (int i=1;i<=n;i++){
        scanf("%s",s);
        insert(s,i);
    }
    getFail();
    //printf("sz=%d\n",sz);
    p[0][1]=1;
    for (int step=0;step<m;step++)
    for (int u=1;u<=sz;u++)
    if (p[step][u])
    for (int v=0;v<3;v++){
        int i=ch[u][v];
        p[step+1][i]=p[step][u];
        f[step+1][i]=max(f[step+1][i],f[step][u]+val[i]);
    }
    //for (int i=1;i<=sz;i++) printf("%d ",val[i]); printf("\n");
    int ans=0;
    /*
    for (int i=0;i<=7;i++)
    for (int j=1;j<=sz;j++)
        printf("f[%d][%d]=%d\n",i,j,f[i][j]);
    */
    for (int i=1;i<=sz;i++) ans=max(ans,f[m][i]);
    printf("%d",ans);
    return 0;
}
//SP10502
//Luogu 3041 
好的,这是一道经典的单调栈问题。题目描述如下: 有 $n$ 个湖,第 $i$ 个湖有一个高度 $h_i$。现在要在这些湖之间挖一些沟渠,使得相邻的湖之间的高度差不超过 $d$。请问最少需要挖多少个沟渠。 这是一道单调栈的典型应用题。我们可以从左到右遍历湖的高度,同时使用一个单调栈来维护之前所有湖的高度。具体来说,我们维护一个单调递增的栈,栈中存储的是湖的下标。假设当前遍历到第 $i$ 个湖,我们需要在之前的湖中找到一个高度最接近 $h_i$ 且高度不超过 $h_i-d$ 的湖,然后从这个湖到第 $i$ 个湖之间挖一条沟渠。具体的实现可以参考下面的代码: ```c++ #include <cstdio> #include <stack> using namespace std; const int N = 100010; int n, d; int h[N]; stack<int> stk; int main() { scanf("%d%d", &n, &d); for (int i = 1; i <= n; i++) scanf("%d", &h[i]); int ans = 0; for (int i = 1; i <= n; i++) { while (!stk.empty() && h[stk.top()] <= h[i] - d) stk.pop(); if (!stk.empty()) ans++; stk.push(i); } printf("%d\n", ans); return 0; } ``` 这里的关键在于,当我们遍历到第 $i$ 个湖时,所有比 $h_i-d$ 小的湖都可以被舍弃,因为它们不可能成为第 $i$ 个湖的前驱。因此,我们可以不断地从栈顶弹出比 $h_i-d$ 小的湖,直到栈顶的湖高度大于 $h_i-d$,然后将 $i$ 入栈。这样,栈中存储的就是当前 $h_i$ 左边所有高度不超过 $h_i-d$ 的湖,栈顶元素就是最靠近 $h_i$ 且高度不超过 $h_i-d$ 的湖。如果栈不为空,说明找到了一个前驱湖,答案加一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值