1. 程式人生 > >Anthem of Berland AC自動機或KMP DP

Anthem of Berland AC自動機或KMP DP

題意:

給你一個帶?的字串S,和一個字串T,問把?替換後最多能匹配多少次T?可以重疊匹配。

題解:

這種肯定是要DP的。

怎麼DP呢?

AC自動機上的DP問題很多,這個也可以用AC自動機。

dp[i][j]表示當前在S串的i位置,在AC自動機的j狀態時能完整匹配T的次數。

 當i是問號時列舉26個字母轉移,不是問號直接轉移到這個字元。

滾動陣列一下即可。

當然KMP也是可以的,因為只有一個串。不過需要手動建一下next陣列,跟AC自動機一樣。

程式碼:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <bitset>
#include <map>
#include <vector>
#include <stack>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <cmath>
#include <ctime>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pii;

const int MOD = 1e9 + 7;
const double PI = acos (-1.);
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 2e5 + 5;

char s[MAXN], t[MAXN];

const int SIGMA_SIZE = 130;
const int MAXNODE = 2e5 + 100;
const int MAXS = 150 + 10;
struct ACautomata {


    int ch[MAXNODE][SIGMA_SIZE];
    int f[MAXNODE];    // fail函式
    int val[MAXNODE];  // 每個字串的結尾結點都有一個非0的val
    int last[MAXNODE]; // 輸出連結串列的下一個結點
    //int cnt[MAXS];
    int sz;

    void init() {
        sz = 1;
        memset(ch[0], 0, sizeof(ch[0]));
        // memset(cnt, 0, sizeof(cnt));
    }

    // 字元c的編號
    inline int idx(char c) {
        return c - 'a';
    }

    // 插入字串。v必須非0
    void insert(char *s, int v) {
        int u = 0, n = strlen(s);
        for(int i = 0; i < n; i++) {
            int c = idx(s[i]);
            if(!ch[u][c]) {
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
        }
        val[u] = v;
    }


    // 計算fail函式
    void getFail() {
        queue<int> q;
        f[0] = 0;
        // 初始化佇列
        for(int c = 0; c < SIGMA_SIZE; c++) {
            int u = ch[0][c];
            if(u) {
                f[u] = 0;
                q.push(u);
                last[u] = 0;
            }
        }
        // 按BFS順序計算fail
        while(!q.empty()) {
            int r = q.front();
            q.pop();
            for(int c = 0; c < SIGMA_SIZE; c++) {
                int u = ch[r][c];
                if(!u) {
                    ch[r][c]=ch[f[r]][c];
                    continue;
                }
                q.push(u);
                int v = f[r];
                while(v && !ch[v][c]) v = f[v];
                f[u] = ch[v][c];
                last[u] = val[f[u]] ? f[u] : last[f[u]];
            }
        }
    }

} ac;

int d[2][MAXN];

int main() {
#ifdef LOCAL
    freopen ("input.txt", "r", stdin);
#endif
    scanf("%s %s", s + 1, t + 1);
    ac.init();
    ac.insert(t + 1, 1);
    ac.getFail();
    int n = strlen(s + 1), m = strlen(t + 1);
    memset(d, -1, sizeof(d));
    d[0][0] = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) d[i & 1][j] = -1;
        for (int j = 0; j <= m; j++) {
            if(d[i & 1 ^ 1][j] == -1) continue;
            if (s[i] == '?') {
                for (int k = 0; k < 26; k++)
                    d[i & 1][ac.ch[j][k]] = max(d[i & 1 ^ 1][j] + (ac.ch[j][k] == m) , d[i & 1][ac.ch[j][k]]);
            } else
            d[i & 1][ac.ch[j][s[i] - 'a']] = max(d[i & 1 ^ 1][j] + (ac.ch[j][s[i] - 'a'] == m) , d[i & 1][ac.ch[j][s[i] - 'a']]);
        }
    }
    int ans = 0;
    for (int i = 0; i <= m; i++) ans = max(ans, d[n & 1][i]);
    printf("%d\n", ans);
    return 0;
}