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 namespace std;

#define maxn 400005
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)

const double pi=acos(-1.0);
const double eps=1e-6;

struct Complex{
    double x,y;
    Complex operator + (Complex a) const{Complex b; return b.x=x+a.x,b.y=y+a.y,b;}
    Complex operator - (Complex a) const{Complex b; return b.x=x-a.x,b.y=y-a.y,b;}
    Complex operator * (Complex a) const{Complex b; return b.x=x*a.x-y*a.y,b.y=x*a.y+y*a.x,b;}
}a[maxn],b[maxn],c[maxn]; 

char s[maxn],t[maxn];
int A[maxn],B[maxn];
int ls,lt,rev[maxn],n,m=1,len,ans[maxn];

void FFT(Complex * x, int n, int f)
{
	F(i,0,n-1) if (rev[i]>i) swap(x[rev[i]],x[i]);
	for (int m=2;m<=n;m<<=1)
	{
		int mid=m>>1;Complex wn; wn.x=cos(2.0*pi/m*f); wn.y=sin(2.0*pi/m*f);
		for (int i=0;i<n;i+=m)
		{
			Complex w; w.x=1.0; w.y=0;
			F(j,0,mid-1)
			{
				Complex a=x[i+j],b=x[i+j+mid]*w;
				x[i+j]=a+b; x[i+j+mid]=a-b;
				w=w*wn;
			}
		}
	}
}

int main()
{
//	freopen("in.txt","r",stdin);
	scanf("%s",s); scanf("%s",t);
	ls=strlen(s); lt=strlen(t);
	n=ls+lt+1;
	while (m<=n) m<<=1,len++; n=m;
	F(i,0,n-1)
	{
		int t=i,ret=0;
		F(j,1,len) ret<<=1,ret|=t&1,t>>=1;
		rev[i]=ret;
	}
	F(i,0,ls-1) A[i]=s[i]-‘a‘+1;
	F(i,0,lt-1) {B[lt-i-1]=t[i]-‘a‘+1;if (t[i]==‘?‘) B[lt-i-1]=0;}

	memset(a,0,sizeof a);
	memset(b,0,sizeof b);
	F(i,0,n-1) a[i].x=1;
	F(i,0,n-1) b[i].x=B[i]*B[i]*B[i];
	FFT(a,n,1); FFT(b,n,1);
	F(i,0,n-1)
		c[i]=a[i]*b[i];

	memset(a,0,sizeof a);
	memset(b,0,sizeof b);
	F(i,0,n-1) a[i].x=2*A[i];
	F(i,0,n-1) b[i].x=B[i]*B[i];
	FFT(a,n,1); FFT(b,n,1);
	F(i,0,n-1)
		c[i]=c[i]-a[i]*b[i];

	memset(a,0,sizeof a);
	memset(b,0,sizeof b);
	F(i,0,n-1) a[i].x=A[i]*A[i];
	F(i,0,n-1) b[i].x=B[i];
	FFT(a,n,1); FFT(b,n,1);
	F(i,0,n-1) c[i]=c[i]+a[i]*b[i];

	FFT(c,n,-1);
	F(i,0,n-1) c[i].x=c[i].x/n;
	int cnt=0;
	F(i,0,ls-lt) if (c[i+lt-1].x<0.5) cnt++;
	printf("%d\n",cnt);
	F(i,0,ls-lt) if (c[i+lt-1].x<0.5) printf("%d\n",i);
}

  

时间: 2024-09-04 17:29:34

BZOJ 4503 两个串 ——FFT的相关文章

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]来表示匹配函数.我们可以发现,如果将

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

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]为对应字符的对应编号,

[bzoj P4504] K个串

[bzoj P4504] K个串 [题目描述] 兔子们在玩k个串的游戏.首先,它们拿出了一个长度为n的数字序列,选出其中的一个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次). 兔子们想知道,在这个数字序列所有连续的子串中,按照以上方式统计其所有数字之和,第k大的和是多少. [输入格式] 第一行,两个整数n和k,分别表示长度为n的数字序列和想要统计的第k大的和 接下里一行n个数a_i,表示这个数字序列 [输出格式] 一行一个整数,表示第k大的和 [样例输入] 7 5

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

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

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