P4173 残缺的字符串

\(\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;
}

原文地址:https://www.cnblogs.com/olinr/p/10270937.html

时间: 2024-07-31 12:24:48

P4173 残缺的字符串的相关文章

Luogu P4173 残缺的字符串

P4173 残缺的字符串 FFT在字符串匹配中的应用. 能解决大概这种问题: 给定长度为\(m\)的A串,长度为\(n\)的B串.问A串在B串中的匹配数 我们设一个函数(下标从\(0\)开始) \(C(x,y) =A(x)- B(y)\),若为0,表示B串中以第\(y\)个字符结尾的字符可以与A串中以\(x\)节为结尾的字符可以匹配 \(P(x) = \sum_{i = 0}^{m - 1}C(i,x - m + i + 1)\) 但是很遗憾当\(P(x)\),等于零时,只能够说明上述子串的字符

P4173 残缺的字符串 fft

题意:给你两个字符串,问你第一个在第二个中出现过多少次,并输出位置,匹配时是模糊匹配*可和任意一个字符匹配 题解:fft加速字符串匹配; 假设上面的串是s,s长度为m,下面的串是p,p长度为n,先考虑没有*的情况那么\(\sum_{j=1}^m(s_{i+j}-p_j)^2=0\)就表示能够从i开始匹配,现在考虑有*的情况,我们只需要让有*的和任意字符匹配即可,那么把公式变成\(\sum_{j=1}^m(s_{i+j}-p_j)^2*s_{i+j}*p_j)=0\),但是fft正向匹配太慢了,我

[Luogu P4173]残缺的字符串 ( 数论 FFT)

题面 传送门:洛咕 Solution 这题我写得脑壳疼,我好菜啊 好吧,我们来说正题. 这题.....emmmmmmm 显然KMP类的字符串神仙算法在这里没法用了. 那咋搞啊(或者说这题和数学有半毛钱关系啊) 我们考虑把两个字符相同强行变为一个数学关系,怎么搞呢? 考虑这题是带通配符的,我们可以这样设: \(C(x,y)=(A[x]-B[y])^2*A[x]*B[y]\) 因此,我们可以看出两个字符一样当且仅当\(C(x,y)=0\) 因此,我们再设一个函数\(P(x)\)表示\(B\)串以第\

P4173 残缺的字符串(FFT)

[Luogu4173] 题解 \(1.\)定义匹配函数 \(2.\)定义完全匹配函数 \(3.\)快速计算每一位的完全匹配函数值 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> #define debug(...) fprintf(stderr,__VA_ARGS__) #define Debug(x) cout&l

luogu P4173 残缺的字符串 FFT

温馨提示:倘若下角标看不清的话您可以尝试放大. 倘若没有通配符的话可以用KMP搞一搞. 听巨佬说通配符可以用FFT搞一搞. 我们先考虑一下没有通配符的怎么搞.我们设a=1,b=2,...,然后我们构造一个这样的函数\(\displaystyle P_x=\sum_{i=0}^{m-1}(A_i-B_{x-m+1+i})^2\),但且仅当A和B在x的位置上匹配完成的时候$P_x $为0.至于为什么是平方,主要是为了防止正数和负数相互抵消. 至于通配符,我们设它为0,我们尝试重新构造一下\(\dis

【BZOJ4259】 残缺的字符串

Description 很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串A和B,其中A串长度为m,B串长度为n.可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺. 你想对这两个串重新进行匹配,其中A为模板串,那么现在问题来了,请回答,对于B的每一个位置i,从这个位置开始连续m个字符形成的子串是否可能与A串完全匹配? Input 第一行包含两个正整数m,n(1<=m<=n<=300000),分别表示A串和B串的长度. 第二行为一个长度为m的

@bzoj - [email&#160;protected] 残缺的字符串

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串A和B,其中A串长度为m,B串长度为n.可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺. 你想对这两个串重新进行匹配,其中A为模板串,那么现在问题来了,请回答,对于B的每一个位置i,从这

BZOJ4259 : 残缺的字符串

假设字符串是从第0位开始的,那么对于两个长度都为n的字符串A,B,定义距离函数\[dis(A,B)=\sum_{i=0}^{n-1}(A[i]-B[i])^2[A[i]!='*'][B[i]!='*']\] 若把*号都设置为0,那么有\[dis(A,B)=\sum_{i=0}^{n-1}(A[i]-B[i])^2A[i]B[i]\] 如果$dis(A,B)=0$,那么A和B完全匹配. 对于这个问题,假设我们枚举B的末尾位置i,设$f[i]=dis(A,B[i-m+1,i])$,那么B的这一个子串

【BZOJ】4259: 残缺的字符串

[题意]给定长度为m的匹配串B和长度为n的模板串A,求B在A中出现多少次.字符串仅由小写字母和通配符" * "组成,其中通配符可以充当任意一个字符.n<=3*10^5. [算法]FFT [题解]假设模板串的数组A用0~26代表所有字符,0为通配符,匹配串的数组B同理,那么用表示差异的经典套路: $$C_n=\sum_{i=0}^{m-1}(A_{n+i}-B_i)^2*A_{n+i}*B_i$$ 那么可以看出$C_n=0$当且仅当$S_A[n,n+m-1]=S_B[0,m-1]$