hdu2457 AC自动机+dp

本文介绍如何使用AC自动机和动态规划(dp)算法解决字符串后缀模式匹配问题,通过计算处理次数的最小值来优化查找过程。实现步骤包括构建 Trie 树,计算 fail 缓存,以及利用 dp 数组进行状态转移。

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


一、题目

二、思路和代码

1.思路

经典的AC自动机+dp (x2)

dp[i][j]表示考虑前i个字符, 且后缀模式为状态 [root, j] 的最少处理次数

2.读入数据

代码如下:

#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 2000;
const int inf = 0x3f3f3f3f;
struct node {
  int next[4];
  int cnt, end;
  void init() {
    memset(next, 0, sizeof(next));
    cnt = 0;
    end = 0;
  }
} trie[maxn];
int cnt, fail[maxn];

int ans;
char str[maxn];
int dp[maxn][maxn];
int n;
void init() {
  cnt = 0, ans = inf;
  memset(fail, 0, sizeof(fail));
  memset(dp, 0x3f, sizeof(dp));
  dp[0][0] = 0;
  trie[0].init();
}
void insert(char *s) {
  int len = strlen(s);
  int u = 0;
  for (int i = 0; i < len; i++) {
    int c;
    if (s[i] == 'A') c = 0;
    if (s[i] == 'C') c = 1;
    if (s[i] == 'G') c = 2;
    if (s[i] == 'T') c = 3;
    if (!trie[u].next[c]) {
      trie[u].next[c] = ++cnt;
      trie[cnt].init();
    }
    u = trie[u].next[c];
  }
  trie[u].end = 1;
  trie[u].cnt++;
}
void getFail() {
  queue<int> q;
  fail[0] = 0;
  for (int i = 0; i < 4; i++) {
    if (trie[0].next[i]) {
      fail[trie[0].next[i]] = 0;
      q.push(trie[0].next[i]);
    }
  }
  while (!q.empty()) {
    int u = q.front();
    q.pop();
    for (int i = 0; i < 4; i++) {
      int v = trie[u].next[i];
      if (!v) {
        trie[u].next[i] = trie[fail[u]].next[i];
      } else {
        fail[v] = trie[fail[u]].next[i];
        q.push(v);
      }
    }
    trie[u].cnt += trie[fail[u]].cnt;
    trie[u].end |= trie[fail[u]].end;
  }
}
int main() {
  //   freopen("in.txt", "r", stdin);
  //   freopen("out.txt", "w", stdout);
  int cas = 0;
  while (scanf("%d", &n) && n) {
    init();
    for (int i = 0; i < n; i++) {
      scanf("%s", str);
      insert(str);
    }
    getFail();
    scanf("%s", str);
    int len = strlen(str);
    for (int i = 0; i < len; i++) {
      for (int j = 0; j <= cnt; j++) {
        for (int k = 0; k < 4; k++) {
          int v = trie[j].next[k];
          if (trie[v].end) continue;
          int c;
          if (str[i] == 'A') c = 0;
          if (str[i] == 'C') c = 1;
          if (str[i] == 'G') c = 2;
          if (str[i] == 'T') c = 3;
          if (c == k) {  // str[i]==trie[j].next[k]
            dp[i + 1][v] = min(dp[i + 1][v], dp[i][j]);
          } else {
            dp[i + 1][v] = min(dp[i + 1][v], dp[i][j] + 1);
          }
        }
      }
    }
    for (int i = 0; i <= cnt; i++) {
      ans = min(ans, dp[len][i]);
    }
    ans = ans == inf ? -1 : ans;
    printf("Case %d: %d\n", ++cas, ans);
  }
  return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值