zoukankan      html  css  js  c++  java
  • bzoj 4503: 两个串【脑洞+FFT】

    真实脑洞题
    因为通配符所以导致t串实际有指数级别个,任何字符串相关算法都没有用
    考虑一个新的匹配方法:设a串(模板串)长为n,从m串的i位置开始匹配:( sum_{i=0}{n-1}(a[j]-b[i+j])2a[j] )
    这个东西只有在从i开始的长为n的a串子串与b串完全匹配的时候才为0,因为首先如果两个字符相同,差的平方和才为0,令t中的'?'值为0,这样某一位为0就是这一位的字符匹配上或者a串的这一位通配
    然后考虑优化这个n^2的东西

    [c[i]=sum_{i=0}^{n-1}(a[j]-b[i+j])^2a[j] ]

    [=sum_{i=0}^{n-1}a[j]^3+a[j]b[i+j]^2-2a[j]^2b[i+j] ]

    [=sum_{i=0}^{n-1}a[j]^3+sum_{i=0}^{n-1}a[j]b[i+j]^2-2sum_{i=0}^{n-1}a[j]^2b[i+j] ]

    前面的是常数,所以我们只需要考虑快速计算形如( sum_{i=0}^{n-1}a[j]b[i+j] ) 的东西即可,设na nb分别是a b数组倒过来(也就是reverse一下)

    [sum_{i=0}^{n-1}a[j]b[i+j] ]

    [=sum_{i=0}^{n-i-1}a[j]b[i+j]+sum_{i=n-i}^{n-1}a[j]b[i+j] ]

    [=sum_{i=0}^{n-i-1}a[j]nb[n-i-1-j]+sum_{i=0}^{i-1}a[n-i+j]b[n+j] ]

    [=sum_{i=0}^{n-i-1}a[j]nb[(n-i-1)-j]+sum_{i=0}^{i-1}na[(i-1)-j]b[n+j] ]

    这样就两部分卷积可以用FFT优化了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    const int N=500005;
    int n,m,bt,lm,re[N],tot;
    double ans[N],sm;
    char s[N],t[N];
    struct cd
    {
    	double a,b;
    	cd(double A=0,double B=0)
    	{
    		a=A,b=B;
    	}
    	cd operator + (const cd &x) const
    	{
    		return cd(a+x.a,b+x.b);
    	}
    	cd operator - (const cd &x) const
    	{
    		return cd(a-x.a,b-x.b);
    	}
    	cd operator * (const cd &x) const
    	{
    		return cd(a*x.a-b*x.b,a*x.b+b*x.a);
    	}
    }a[N],b[N],a2[N],b2[N],na[N],nb[N],na2[N],nb2[N];
    void dft(cd a[],int f)
    {
    	for(int i=0;i<lm;i++)
    		if(i<re[i])
    			swap(a[i],a[re[i]]);
    	for(int i=1;i<lm;i<<=1)
    	{
    		cd wi=cd(cos(M_PI/i),f*sin(M_PI/i));
    		for(int k=0;k<lm;k+=(i<<1))
    		{
    			cd w=cd(1,0),x,y;
    			for(int j=0;j<i;j++)
    			{
    				x=a[j+k],y=w*a[i+j+k];
    				a[j+k]=x+y,a[i+j+k]=x-y;
    				w=w*wi;
    			}
    		}
    	}
    	if(f==-1)
    		for(int i=0;i<lm;i++)
    			a[i].a/=lm;
    }
    void fft(cd a[],cd b[])
    {
    	dft(a,1);
    	dft(b,1);
    	for(int i=0;i<lm;i++)
    		a[i]=a[i]*b[i];
    	dft(a,-1);
    }
    int main()
    {
    	scanf("%s%s",s,t);
    	m=strlen(s),n=strlen(t);
    	for(int i=0;i<n;i++)
    		a[i].a=(t[i]=='?')?0:t[i]-'a'+1,a2[i].a=a[i].a*a[i].a,sm+=a[i].a*a[i].a*a[i].a;
    	for(int i=0;i<m;i++)
    		b[i].a=s[i]-'a'+1,b2[i].a=b[i].a*b[i].a;
    	for(int i=0;i<m;i++)
    		na[i]=a[m-i-1],nb[i]=b[m-i-1],na2[i]=a2[m-i-1],nb2[i]=b2[m-i-1];
    	for(bt=0;(1<<bt)<=2*m;bt++);
    	lm=1<<bt;
    	for(int i=0;i<lm;i++)
    		re[i]=(re[i>>1]>>1)|((i&1)<<(bt-1));
    	fft(a,nb2);
    	fft(b2,na);
    	fft(a2,nb);
    	fft(b,na2);
    	for(int i=0;i<=m-n;i++)
    	{
    		ans[i]=sm+(int)(a[m-i-1].a+0.5)-2*(int)(a2[m-i-1].a+0.5);
    		if(i>0)
    			ans[i]+=(int)(b2[i-1].a+0.5)-2*(int)(b[i-1].a+0.5);
    		tot+=(ans[i]==0);
    	}
    	printf("%d
    ",tot);
    	for(int i=0;i<=m-n;i++)
    		if(ans[i]==0)
    			printf("%d ",i);
    	return 0;
    }
    
  • 相关阅读:
    JS年月日三级联动下拉列表
    日志分析软件
    配置Smarty
    JS无刷新省市两级联动下拉列表
    graylog2+syslogng+mongodb构建集中管理日志服务器
    syslog及syslogng详解
    php+pdo实现分页类代码
    编程实践62
    编程实践65
    编程实践64
  • 原文地址:https://www.cnblogs.com/lokiii/p/10032311.html
Copyright © 2011-2022 走看看