HDU 4057 Rescue the Rabbit(AC自动机+DP)

题目链接

题意:给定n个模式串,每个模式串都有权值。文本串如果包含某个模式串,就加上该模式串的权值(如果某个模式串多次出现,只计算一次的贡献),求所有长度为L的文本串中权值最大的是多少。

AC自动机+DP 的入门题。n最大只有10,用二进制状态压缩来表示n个模式串的出现情况。dp[i][j][k] i代表当前文本串长度,j代表当前在AC自动机上第 j个结点,k就是二进制压缩的状态,表示当前n个串的出现情况。DP数组的值为0或1,表示对应的状态是否存在。最后遍历所有二进制压缩的状态,计算其贡献并取最大值。DP起点设置:dp[0][0][0]=1 , 对应空串的状态,长度为0,对应自动机上初始结点,匹配了0个模式串。

细节:1.空间上有限制,需要使用滚动数组,交替使用。dp[i]的状态都是从dp[i-1]转移过来,即长度为i的文本串是通过长度为(i-1)的文本串加上一个字符构成的。所以数组第一维大小只需要2,交替使用即可。一个作为上一次的状态,一个作为当前新的状态。

2.模式串可能重复,插入的时候不能直接覆盖,要累加。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 5;
// AC自动机
int getId(char ch)
{
   if (ch == 'A')
      return 0;
   if (ch == 'T')
      return 1;
   if (ch == 'G')
      return 2;
   return 3;
}
queue<int> q;
int c[MAXN][4], val[MAXN], fail[MAXN], cnt;
void ins(char *s, int index)
{
   int len = strlen(s);
   int now = 0;
   for (int i = 0; i < len; i++)
   {
      int v = getId(s[i]);
      if (!c[now][v])
         c[now][v] = ++cnt;
      now = c[now][v];
   }
   val[now] |= 1 << index; // now 这个结点 代表 的 是第index 个 模式串 二进制状态表示
}
void get_fail_pointer()
{
   for (int i = 0; i < 4; i++)
      if (c[0][i])
         fail[c[0][i]] = 0, q.push(c[0][i]);
   while (!q.empty())
   {
      int u = q.front();
      q.pop();
      for (int i = 0; i < 4; i++)
         if (c[u][i])
            fail[c[u][i]] = c[fail[u]][i], q.push(c[u][i]), val[c[u][i]] |= val[fail[c[u][i]]];
         else
            c[u][i] = c[fail[u]][i];
   }
}

char s[123];
int w[123];
int dp[2][1011][1 << 11];
int cal(int sta)
{
   int res=0;
   int cnt=0;
   while(sta){
      if(sta&1)res+=w[cnt];
      sta>>=1;
      cnt++;
   }
   return res;
}
int main()
{
#ifdef DEBUG
   freopen("1.in", "r", stdin);
   freopen("1.out", "w", stdout);
#endif
   int n, L;
   while (~scanf("%d%d", &n, &L))
   {
      memset(c,0,sizeof(c));
      memset(dp,0,sizeof(dp));
      memset(val,0,sizeof(val));
      cnt=0;
      for (int i = 0; i < n; i++)
      {
         scanf("%s %d", s, &w[i]);
         ins(s, i);
      }
      get_fail_pointer();
      dp[0][0][0] = 1;
      for (int i = 1; i <= L; i++)
      {
         memset(dp[i & 1], 0, sizeof(dp[i & 1]));
         for (int j = 0; j <= cnt; j++)
         {
            for (int i2 = 0; i2 < (1 << n); i2++)
            {
               for (int k = 0; k < 4; k++)
               {
                  if (dp[(i - 1) & 1][j][i2])
                  {
                     dp[i & 1][c[j][k]][i2 | val[c[j][k]]] = 1;
                  }
               }
            }
         }
      }
      int ans = -1e9;

      for (int i2 = 0; i2 < (1 << n); i2++)
      {
         for (int j = 0; j <= cnt; j++)
         {
            if (dp[L & 1][j][i2])
            {
               ans=max(cal(i2),ans);
               break;
            }
         }
      }
      if(ans>=0)
         printf("%d\n",ans);
      else 
         puts("No Rabbit after 2012!");
   }

   return 0;
}
上一篇:NYOJ 1272 表达式求值 第九届省赛 (字符串处理)


下一篇:C#设计一个简单的计算器,实现两个数的加,减,乘,除,求幂等计算,运行效果如下图所示: