[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\)串以第\(x\)项为结尾的长度为\(m\)的子串是否与\(A\)串匹配,显然有:
\(P(x)=\sum_{i=0}^{m-1}C(i,x-m+i+1)\)
\(P(x)=\sum_{i=0}^{m-1}(A[i]-B[x-m+i+1])^2*A[i]*B[x-m+i+1]\)

后面那个式子写的太蛋疼了,我们把\(x-m+i+1\)设为\(j\)吧。
\(P(x)=\sum_{i=0}^{m-1}(A[i]-B[j])^2*A[i]*B[j]\)

大力展开得:
\(P(x)=\sum_{i=0}^{m-1}A[i]^3B[j]-2A[i]^2B[j]^2+A[i]B[j]^3\)

然后.....我们试着把\(\sum\)展开?
\(P(x)=\sum_{i=0}^{m-1}A[i]^3B[j]-\sum_{i=0}^{m-1}2A[i]^2B[j]^2+\sum_{i=0}^{m-1}A[i]B[j]^3\)

还是没法算啊......
诶,等下,如果我们把\(A\)串反转为\(A'\),肯定有\(A[i]=A'[m-i-1]\)
然后这个\((m-i-1)+(j)\)......不就是等于\(x\)嘛。
所以说我们马上就有:
\(P(x)=\sum_{i+j=x}A[i]^3B[j]-\sum_{i+j=x}2A[i]^2B[j]^2+\sum_{i+j=x}A[i]B[j]^3\)

哦豁,卷积,搞定~
时间复杂度\(O(nlogn)\)

搞定个鬼,这题要做7次FFT,常数爆大,我卡不进去

还请各位dalao赐教。


Code

#include<iostream>
#include<cstdio>
#include<complex>
#include<cmath>
#include<algorithm>
using namespace std;
const int M=300000+100;
const int N=M*4;
const double PI=acos(-1);
const double eps=1e-1;
typedef complex <double> cp;
cp omega(int K,int n)
{
    return cp(cos(2*PI*K/n),sin(2*PI*K/n));
}
inline void FFT(cp a[],int n,bool type)
{
    static int tmp[N],num=n-1,len;
    while(num!=0) num/=2,len++;
    for(int i=0,j;i<=n;i++)
    {
        for(j=0,num=i;j<len;j++)
            tmp[j]=num%2,num/=2;
        reverse(tmp,tmp+len);
        for(j=0,num=0;j<len;j++)
            num+=tmp[j]*(1<<j);
        if(i<num) swap(a[i],a[num]);
    }
    for(int l=2;l<=n;l*=2)
    {
        int m=l/2;
        cp x0=omega(1,l);
        if(type==true) x0=conj(x0);
        for(int i=0;i<n;i+=l)
        {
            cp x(1,0);
            for(int j=0;j<m;j++,x*=x0)
            {
                cp temp=x*a[i+j+m];
                a[i+j+m]=a[i+j]-temp;
                a[i+j]=a[i+j]+temp;
            }
        }
    }
}
char A[N],B[N];
int m,n,t=1;
cp S1[N],S2[N],S3[N],B1[N],B2[N],B3[N];
bool OK[N];
int main()
{
    scanf("%d%d%s%s",&m,&n,A,B);

    while(t<=(n+m)) t*=2;
    reverse(A,A+m);
    for(int i=0;i<t;i++)
        A[i]=(A[i]=='*' or A[i]<'a' or A[i]>'z'?0:A[i]-'a'+1),
        B[i]=(B[i]=='*' or B[i]<'a' or B[i]>'z'?0:B[i]-'a'+1);
    for(int i=0;i<t;i++)
        S1[i]=A[i],S2[i]=A[i]*A[i],S3[i]=A[i]*A[i]*A[i];
    for(int i=0;i<t;i++)
        B1[i]=B[i],B2[i]=B[i]*B[i],B3[i]=B[i]*B[i]*B[i];
    FFT(S1,t,false);
    FFT(S2,t,false);
    FFT(S3,t,false);
    FFT(B1,t,false);
    FFT(B2,t,false);
    FFT(B3,t,false);

    for(int i=0;i<t;i++)
        S3[i]*=B1[i];
    for(int i=0;i<t;i++)
        S1[i]*=B3[i];
    for(int i=0;i<t;i++)
        S2[i]*=B2[i];
    for(int i=0;i<t;i++)
        S3[i]+=S1[i]-2.0*S2[i];
    FFT(S3,t,true);
    int cnt=0;
    for(int i=m-1;i<n;i++)
        if(fabs(S3[i].real()/t)<eps)
            OK[i]=true,cnt++;

    printf("%d\n",cnt);
    for(int i=m-1;i<n;i++)
        if(OK[i]==true)
            printf("%d ",i-m+1+1);
    return 0;
}

原文地址:https://www.cnblogs.com/GoldenPotato/p/10294437.html

时间: 2024-07-29 22:00:55

[Luogu P4173]残缺的字符串 ( 数论 FFT)的相关文章

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)\),等于零时,只能够说明上述子串的字符

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

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

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正向匹配太慢了,我

P4173 残缺的字符串

\(\color{#0066ff}{ 题目描述 }\) 很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串\(A\)和\(B\),其中\(A\)串长度为\(m\),\(B\)串长度为\(n\).可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有不同程度的残缺. 你想对这两个串重新进行匹配,其中\(A\)为模板串,那么现在问题来了,请回答,对于\(B\)的每一个位置\(i\),从这个位置开始连续\(m\)个字符形成的子串是否可能与\(A\)串完全匹配? \(\col

bzoj 4259 4259: 残缺的字符串【FFT】

和bzoj 4503 https://www.cnblogs.com/lokiii/p/10032311.html 差不多,就是再乘上一个原串字符 有点卡常,先在点值下算最后一起IDFT #include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int N=1100005; int n,m,bt,lm,re[N],tot;

luogu P3709 大爷的字符串题

二次联通门 : luogu P3709 大爷的字符串题 /* luogu P3709 大爷的字符串题 莫队 看了半天题目 + 题解 才弄懂了要求什么... 维护两个数组 一个记录数字i出现了几次 一个记录出现了i次的有几个数.. */ #include <algorithm> #include <cstdlib> #include <cstdio> #include <cmath> #define Max 200090 void read (int &

BZOJ 4259 残缺的字符串 ——FFT

[题目分析] 同bzoj4503. 只是精度比较卡,需要试一试才能行O(∩_∩)O 用过long double,也加过0.4.最后发现判断的时候改成0.4就可以了 [代码] #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 1200005 #

【BZOJ4259】 残缺的字符串

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