BZOJ4503 两个串 FFT

题目传送门 - BZOJ4503


题意概括

  给定两个字符串S和T,回答T在S中出现了几次,在哪些位置出现。注意T中可能有?字符,可以匹配任何字符。


题解

  首先,假装你已经知道了这是一道$FFT$题。

  考虑怎样$FFT$。

  字符串匹配的时候,对于匹配成功的对应字母的编号(比如分别是$i$和$j$),满足了$i-j$都相同。但是我们需要的是$i+j$都相等。

  于是我们用$FFT$的经典套路,翻转$T$串。

  我们构造一个卷积:

  $\sum_{i=0}^{n}\sum_{j=0}^{m}(S_{i}-T_{j})^{2}S_{i}T_{j}$

  其中对应的字符$c$如果为‘?‘值为$0$,否则为$c-‘a‘+1$。

  这样的话,如果对应位算出来是$0$的话那么就可以匹配。

  那么我们考虑求解这个式子。

  我们只要展开一下:

  $(S_i-T_j)^{2}S_{i}T_{j}\ = \ s_{i}^{3}t_{j}-2s_{i}^{2}t_{j}^{2}+s_{i}t_{j}^{3}$

  然后变成了三组卷积,一坨$FFT$即可。

  $Time:9000+ MS$

  震惊!

  本题还有更优秀的解法。

  对于没有问号的,我们$KMP$解决。

  对于有问好的,暴搜解决。

  复杂度极其优越。

  $Time:100- MS$


代码

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <vector>
using namespace std;
const int N=1<<18;
const double PI=acos(-1.0);
struct C{
	double r,i;
	C(){r=i=0;}
	C(double a,double b){r=a,i=b;}
	C operator + (C a){return C(r+a.r,i+a.i);}
	C operator - (C a){return C(r-a.r,i-a.i);}
	C operator * (C a){return C(r*a.r-i*a.i,r*a.i+i*a.r);}
}a[N],b[N],a1[N],b1[N],a2[N],b2[N],a3[N],b3[N],w[N];
int A,B,n,L,res[N],R[N];
double tot[N];
vector <int> ans;
char s[N],t[N];
void FFT (C a[N],int n){
	for (int i=0;i<n;i++)
		if (i<R[i])
			swap(a[i],a[R[i]]);
	for (int d=1,t=n>>1;d<n;d<<=1,t>>=1)
		for (int i=0;i<n;i+=(d<<1))
			for (int j=0;j<d;j++){
				C tmp=w[t*j]*a[i+j+d];
				a[i+j+d]=a[i+j]-tmp;
				a[i+j]=a[i+j]+tmp;
			}
}
int main(){
	scanf("%s%s",s,t);
	A=strlen(s),B=strlen(t);
	for (int i=0;i<B/2;i++)
		swap(t[i],t[B-i-1]);
	// (s-t)(s-t)st
	//=ssst-2sstt+sttt
	for (int i=0;i<n;i++)
		a[i]=b[i]=C(0,0);
	for (int i=0;i<A;i++)
		a[i].r=s[i]-‘a‘+1;
	for (int i=0;i<B;i++)
		b[i].r=t[i]==‘?‘?0:(t[i]-‘a‘+1);
	for (n=1,L=0;n<=A+B;n<<=1,L++);
	for (int i=0;i<n;i++){
		R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
		w[i]=C(cos(2*i*PI/n),sin(2*i*PI/n));
		a1[i]=a[i]*a[i]*a[i];
		b1[i]=b[i];
		a2[i]=a[i]*a[i];
		b2[i]=b[i]*b[i];
		a3[i]=a[i];
		b3[i]=b[i]*b[i]*b[i];
	}
	FFT(a1,n),FFT(b1,n),FFT(a2,n),FFT(b2,n),FFT(a3,n),FFT(b3,n);
	for (int i=0;i<n;i++){
		a1[i]=a1[i]*b1[i];
		a2[i]=a2[i]*b2[i];
		a3[i]=a3[i]*b3[i];
		w[i].i*=-1.0;
	}
	FFT(a1,n),FFT(a2,n),FFT(a3,n);
	for (int i=0;i<n;i++)
		tot[i]=a1[i].r-2.0*a2[i].r+a3[i].r;
	for (int i=0;i<n;i++)
		res[i]=int(tot[i]+0.5);
	ans.clear();
	for (int i=B-1;i<A;i++)
		if (!res[i])
			ans.push_back(i-B+1);
	printf("%d\n",ans.size());
	for (vector <int>::iterator i=ans.begin();i!=ans.end();i++)
		printf("%d\n",*i);
	return 0;
}

  

原文地址:https://www.cnblogs.com/zhouzhendong/p/8536065.html

时间: 2024-07-29 18:42:04

BZOJ4503 两个串 FFT的相关文章

bzoj4503: 两个串 bitset

题目链接 bzoj4503: 两个串 题解 暴一发bitset f[i][j] 表示 S[1..i] 是否有个后缀能匹配 T[1..j] 那么假设 S[i+1] 能匹配 T[s],令 f[i+1][s] | = f[i][s-1] 所以预处理理出每个字符能匹配 T的哪些位置,设为[c] 那么 f[i]=((f[i-1]<<1)|(1<<1)) & mat[S[i]] 直接在mat上做匹配就好了 时间复杂度:O(|S||T|/32) 代码 #include<cstdio

bzoj4503 两个串

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4503 [题解] 我们设匹配函数f = (a[i]-b[i])^2*b[i] 那么展开f,做卷积就能得出f的值了 对于t[i]=='?',b[i]=0,显然当f=0表示匹配,那么直接FFT即可. 我记得有道类似的题,两串都有通配符匹配,就把f改成(a[i]-b[i])^2a[i]b[i]就ok了. # include <math.h> # include <stdio.h> #

BZOJ 4503 两个串 ——FFT

[题目分析] 定义两个字符之间的距离为 (ai-bi)^2*ai*bi 如果能够匹配,从i到i+m的位置的和一定为0 但这和暴力没有什么区别. 发现把b字符串反过来就可以卷积用FFT了. 听说KMP+暴力可以卡到100ms以内(雾) [代码] #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using na

bzoj4403 两个串

Description 兔子们在玩两个串的游戏.给定两个字符串S和T,兔子们想知道T在S中出现了几次, 分别在哪些位置出现.注意T中可能有“?”字符,这个字符可以匹配任何字符. Input 两行两个字符串,分别代表S和T Output 第一行一个正整数k,表示T在S中出现了几次 接下来k行正整数,分别代表T每次在S中出现的开始位置.按照从小到大的顺序输出,S下标从0开始. 两个串a,b相等(b中有通配符)当且仅当Σ(a[i]-b[i])2b[i]=0,其中a[i],b[i]为对应字符的对应编号,

UESTC 883 方老师与两个串

CF原题 由题可知,n,m太大,无法开出dp[n][m]的数组. 观察发现s/e最大为300,也就是说,选用第一种操作的次数不会超过300. 于是定义dp[i][j],第一个串的前i个数,使用了j次第一种操作的时候,第二个串最少删了多少个数. 转移有两种情况: 1.当前位置不删,这时dp[i][j]=dp[i-1][j]: 2.当前位置删,此时就需要在B串中找和当前位置的数相同的数的位置,并且只有在找到的位置大于dp[i-1][j-1]的时候才是可行的.为了保证dp[i][j]最小,显然就是找大

数据结构——算法之(032)(求两个串中的第一个最长子串)

[申明:本文仅限于自我归纳总结和相互交流,有纰漏还望各位指出. 联系邮箱:[email protected]] 题目: 求两个串中的第一个最长子串(神州数码曾经试题).如"abractyeyt","dgdsaeactyey"的最大子串为"actyey". 题目分析: 1.这里仅仅是实现了简单的字符串算法(最大支持字符串长度64),主要是展示算法思想 2.思路是把2个字符串每一个字符的匹配关系,映射到一张二维数组表中,匹配写1,非匹配写0 算法实现

求两个串的最大子序列(非字串)

问题:求两个串的最大子序列(并非连接的) Java代码: import java.util.Set; import java.util.StringJoiner; public class Main { public static int getL(String a, String b) { if (a.isEmpty()||b.isEmpty()) return 0; if (a.charAt(0) == b.charAt(0)) return getL(a.substring(1), b.s

SPOJ 1811 Longest Common Substring(求两个串的最长公共子串)

http://www.spoj.com/problems/LCS/ 题目:求两个串的最长公共子串 分析: 以A建立SAM 让B在SAM上匹配可以类比于kmp思想,我们知道在Parent树上,fa是当前节点的子集,也就是说满足最大前缀,利用这个就可以做题了 #include <bits/stdc++.h> #define LL long long #define P pair<int, int> #define lowbit(x) (x & -x) #define mem(a

BZOJ 4503 两个串(FFT)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4503 [题目大意] 给出S串和T串,计算T在S中出现次数,T中有通配符'?'. [题解] 我们定义f[x]=sum_{i=0}^{n-1}|s1[i]-s2[i]|,当f[x]=0时,两个字符串相等.因为考虑到这里还有适配符,所以用f[x]=sum_{i=0}^{n-1}(s1[i]-s2[i])*(s1[i]-s2[i])*s1[i]*s2[i]来表示匹配函数.我们可以发现,如果将