1. 程式人生 > >P4173 殘缺的字符串

P4173 殘缺的字符串

tle 個數 -o2 its push turn 構造 get c++

\(\color{#0066ff}{ 題目描述 }\)

很久很久以前,在你剛剛學習字符串匹配的時候,有兩個僅包含小寫字母的字符串\(A\)\(B\),其中\(A\)串長度為\(m\)\(B\)串長度為\(n\)。可當你現在再次碰到這兩個串時,這兩個串已經老化了,每個串都有不同程度的殘缺。

你想對這兩個串重新進行匹配,其中\(A\)為模板串,那麽現在問題來了,請回答,對於\(B\)的每一個位置\(i\),從這個位置開始連續\(m\)個字符形成的子串是否可能與\(A\)串完全匹配?

\(\color{#0066ff}{輸入格式}\)

第一行包含兩個正整數\(m\),\(n\),分別表示\(A\)串和\(B\)

串的長度。

第二行為一個長度為\(m\)的字符串\(A\)

第三行為一個長度為\(n\)的字符串\(B\)

兩個串均僅由小寫字母和*號組成,其中*號表示相應位置已經殘缺。

\(\color{#0066ff}{輸出格式}\)

第一行包含一個整數\(k\),表示BB串中可以完全匹配\(A\)串的位置個數。

\(k > 0\),則第二行輸出\(k\)個正整數,從小到大依次輸出每個可以匹配的開頭位置(下標從\(1\)開始)。

\(\color{#0066ff}{輸入樣例}\)

3 7
a*b
aebr*ob

\(\color{#0066ff}{輸出樣例}\)

2
1 5

\(\color{#0066ff}{數據範圍與提示}\)

100%的數據滿足\(1 \leq m \leq n \leq 300000\)

\(\color{#0066ff}{ 題解 }\)

構造兩個序列

如果字符為*,則序列對應位置為0, 否則為字符ASCII碼

這樣如果某位置匹配,當且僅當\(\sum (a_i-b_j)^2*a_i*b_j=0\)

為什麽要平方? 因為有可能上下一個是ab,一個是ba,這樣一個正一個負,加完後還是0,但是不匹配

為什麽不用絕對值? 不好處理

這樣這個式子可以展開

\(\sum a_i^3b_{j-i}+\sum a_ib_{j-i}^3-\sum a_i^2b_{j-i}^2\)

分別fft一下就行了

註意最後輸出的時候,循環的範圍,要合法!

這題還卡常,不開O2下面的代碼TLE。。。

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
using std::vector;
const int maxn = 2e6 + 10;
const int mod = 998244353;
int r[maxn], len;
LL ksm(LL x, LL y) {
    LL re = 1LL;
    while(y) {
        if(y & 1) re = re * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return re;
}
void FNTT(vector<int> &A, int len, int flag) {
    A.resize(len);
    for(int i = 0; i < len; i++) if(i < r[i]) std::swap(A[i], A[r[i]]);
    for(int l = 1; l < len; l <<= 1) {
        int w0 = ksm(3, (mod - 1) / (l << 1));
        for(int i = 0; i < len; i += (l << 1)) {
            int w = 1, a0 = i, a1 = i + l;
            for(int k = 0; k < l; k++, a0++, a1++, w = 1LL * w * w0 % mod) {
                int tmp = 1LL * A[a1] * w % mod;
                A[a1] = ((A[a0] - tmp) % mod + mod) % mod;
                A[a0] = (A[a0] + tmp) % mod;
            }
        }
    }
    if(!(~flag)) {
        std::reverse(A.begin() + 1, A.end());
        int inv = ksm(len, mod - 2);
        for(int i = 0; i < len; i++) A[i] = 1LL * A[i] * inv % mod;
    }
}
vector<int> operator * (vector<int> A, vector<int> B) {
    int tot = A.size() + B.size() - 1;
    FNTT(A, len, 1);
    FNTT(B, len, 1);
    vector<int> ans;
    ans.resize(len);
    for(int i = 0; i < len; i++) ans[i] = 1LL * A[i] * B[i] % mod;
    FNTT(ans, len, -1);
    ans.resize(tot);
    return ans;
}
char s[maxn], t[maxn];
int ss[maxn], tt[maxn];
signed main() {
    int m = in(), n = in();
    scanf("%s", s);
    scanf("%s", t);
    for(int i = 0; i < m; i++) ss[i] = s[i] == '*'? 0 : (int)s[i];
    for(int i = 0; i < n; i++) tt[i] = t[i] == '*'? 0 : (int)t[i];
    vector<int> A1, A2, A3, B1, B2, B3, ans, ls;
    for(int i = 0; i < m; i++) {
        A1.push_back(ss[i]);
        A2.push_back(ss[i] * ss[i]);
        A3.push_back(ss[i] * ss[i] * ss[i]);
    }
    for(int i = 0; i < n; i++) {
        B1.push_back(tt[i]);
        B2.push_back(tt[i] * tt[i]);
        B3.push_back(tt[i] * tt[i] * tt[i]);
    }
    std::reverse(A1.begin(), A1.end());
    std::reverse(A2.begin(), A2.end());
    std::reverse(A3.begin(), A3.end());
    int tot = A1.size() + B1.size() - 1;
    for(len = 1; len <= tot; len <<= 1);
    for(int i = 1; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) * (len >> 1));
    ls = A3 * B1;
    for(int i = 0; i < n + m - 1; i++) ans.push_back(ls[i]);
    ls = A1 * B3;
    for(int i = 0; i < n + m - 1; i++) ans[i] = (ans[i] + ls[i]) % mod;
    ls = A2 * B2;
    for(int i = 0; i < n + m - 1; i++) ans[i] = ((ans[i] - 2LL * ls[i]) % mod + mod) % mod;
    tot = 0;
    for(int i = m - 1; i < n; i++) if(!ans[i]) tot++;
    printf("%d\n", tot);
    for(int i = m - 1; i < n; i++) if(!ans[i]) printf("%d ", i - m + 2);
    return 0;
}

P4173 殘缺的字符串